]> git.meshlink.io Git - meshlink/blob - test/blackbox/run_blackbox_tests/test_cases_channel_set_poll_cb.c
Add channel poll callback corner cases
[meshlink] / test / blackbox / run_blackbox_tests / test_cases_channel_set_poll_cb.c
1 /*
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>
4
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.
9
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.
14
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.
18 */
19
20 #ifdef NDEBUG
21 #undef NDEBUG
22 #endif
23
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"
30 #include <assert.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <setjmp.h>
36 #include <cmocka.h>
37 #include <pthread.h>
38 #include <linux/limits.h>
39
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 */
43 #define PORT 8000
44
45 #define NUT                         "nut"
46 #define PEER                        "peer"
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)
49
50 typedef struct test_cb_data {
51         size_t cb_data_len;
52         size_t cb_total_data_len;
53         int total_cb_count;
54         void (*cb_handler)(void);
55         bool cb_flag;
56 } test_cb_t;
57
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);
67
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",
70 };
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",
73 };
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",
76 };
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",
79 };
80
81 static bool polled;
82 static bool reachable;
83
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;
88
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) {
91         (void)mesh;
92         (void)channel;
93         (void)port;
94         (void)data;
95         (void)len;
96         return false;
97 }
98
99 static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
100         (void)len;
101         meshlink_set_channel_poll_cb(mesh, channel, NULL);
102         pthread_mutex_lock(&poll_lock);
103         polled = true;
104         assert(!pthread_cond_broadcast(&poll_cond));
105         pthread_mutex_unlock(&poll_lock);
106 }
107
108 static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *source, bool reach) {
109         (void)mesh;
110         (void)source;
111
112         if(!reach) {
113                 return;
114         }
115
116         pthread_mutex_lock(&reachable_lock);
117         reachable = true;
118         assert(!pthread_cond_broadcast(&reachable_cond));
119         pthread_mutex_unlock(&reachable_lock);
120 }
121
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);
125 }
126 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 1
127
128     Test Steps:
129     1. Run NUT
130     2. Open channel of the NUT itself
131
132     Expected Result:
133     Opens a channel and also invokes poll callback.
134 */
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);
141
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);
151
152         /* Export and Import on both sides */
153         reachable = false;
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));
160
161         assert(meshlink_start(mesh1));
162         assert(meshlink_start(mesh2));
163
164         timeout.tv_sec = time(NULL) + 10;
165         pthread_mutex_lock(&reachable_lock);
166
167         while(reachable == false) {
168                 assert(!pthread_cond_timedwait(&reachable_cond, &reachable_lock, &timeout));
169         }
170
171         pthread_mutex_unlock(&reachable_lock);
172
173         meshlink_node_t *destination = meshlink_get_node(mesh2, "nut");
174         assert(destination != NULL);
175
176         /* Open channel for nut node from bar node which should be accepted */
177         polled = false;
178         meshlink_channel_t *channel = meshlink_channel_open(mesh2, destination, PORT, NULL, NULL, 0);
179         assert(channel);
180         meshlink_set_channel_poll_cb(mesh2, channel, poll_cb);
181
182         timeout.tv_sec = time(NULL) + 10;
183         pthread_mutex_lock(&poll_lock);
184
185         while(polled == false) {
186                 assert(!pthread_cond_timedwait(&poll_cond, &poll_lock, &timeout));
187         }
188
189         pthread_mutex_unlock(&poll_lock);
190
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"));
196
197         return true;
198 }
199
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);
203 }
204 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 2
205
206     Test Steps:
207     1. Run NUT
208     2. Open channel of the NUT itself
209     3. Pass NULL as mesh handle argument for meshlink_set_channel_poll_cb API
210
211     Expected Result:
212     Reports error accordingly by returning NULL
213 */
214 static bool test_steps_channel_set_poll_cb_02(void) {
215         meshlink_set_log_cb(NULL, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
216
217         /* Create meshlink instance */
218         meshlink_handle_t *mesh_handle = meshlink_open("channelpollconf3", "nut", "node_sim", 1);
219         assert(mesh_handle);
220         meshlink_set_log_cb(mesh_handle, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
221
222         assert(meshlink_start(mesh_handle));
223
224         meshlink_node_t *node = meshlink_get_self(mesh_handle);
225         assert(node != NULL);
226
227         /* Opening channel */
228         meshlink_channel_t *channel = meshlink_channel_open(mesh_handle, node, PORT, NULL, NULL, 0);
229         assert(channel != NULL);
230
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);
234
235         meshlink_close(mesh_handle);
236         assert(meshlink_destroy("channelpollconf3"));
237         return true;
238 }
239
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);
243 }
244 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 3
245
246     Test Steps:
247     1. Run NUT
248     2. Open channel of the NUT itself
249     3. Pass NULL as channel handle argument for meshlink_set_channel_poll_cb API
250
251     Expected Result:
252     Reports error accordingly by returning NULL
253 */
254 static bool test_steps_channel_set_poll_cb_03(void) {
255         meshlink_set_log_cb(NULL, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
256
257         /* Create meshlink instance */
258         meshlink_handle_t *mesh_handle = meshlink_open("channelpollconf4", "nut", "node_sim", 1);
259         assert(mesh_handle);
260         meshlink_set_log_cb(mesh_handle, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger);
261
262         assert(meshlink_start(mesh_handle));
263
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);
267
268         meshlink_close(mesh_handle);
269         assert(meshlink_destroy("channelpollconf4"));
270         return true;
271 }
272
273 static test_cb_t poll_cb_data;
274 static test_cb_t recv_cb_data;
275 static meshlink_handle_t *mesh;
276
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;
280
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()
285
286         sleep(1);
287
288         // Make sure there is change in the cumulative poll callbacks count
289
290         if(!poll_cb_last_count) {
291                 poll_cb_last_count = poll_cb_data.total_cb_count;
292         } else {
293                 assert(poll_cb_data.total_cb_count > poll_cb_last_count);
294         }
295
296         // Set the receive callback block flag and reset it back after 2 seconds sleep
297
298         recv_cb_data.cb_flag = true;
299         sleep(2);
300         recv_cb_data.cb_flag = false;
301 }
302
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) {
306
307         // Stop the NUT's meshlink instance, set the receive callback flag indicating that further
308         // poll callbacks are considered to be invalid
309
310         meshlink_stop(mesh);
311         recv_cb_data.cb_flag = true;
312
313         // Reset the callback handler (i.e, this is a one-time operation)
314
315         recv_cb_data.cb_handler = NULL;
316 }
317
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) {
321         sleep(5);
322         recv_cb_data.cb_handler = NULL;
323         recv_cb_data.cb_flag = false;
324 }
325
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);
329 }
330
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) {
333         (void)mesh;
334         (void)channel;
335         (void)data;
336
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;
341
342         if(recv_cb_data.cb_handler) {
343                 recv_cb_data.cb_handler();
344         }
345 }
346
347 /* NUT's poll callback handler */
348 static void poll_cb2(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
349         (void)mesh;
350         (void)channel;
351
352         // printf("Poll len: %zu\n", len);
353         assert(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;
357
358         if(poll_cb_data.cb_handler) {
359                 (poll_cb_data.cb_handler)();
360         }
361 }
362
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) {
365         (void)port;
366         (void)data;
367         (void)len;
368
369         channel->node->priv = channel;
370         meshlink_set_channel_receive_cb(mesh, channel, receive_cb);
371         return true;
372 }
373
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);
377 }
378 /* Test Steps for meshlink_channel_set_poll_cb Test Case # 4
379
380     Test Scenarios:
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.
391 */
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);
397
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);
403
404         link_meshlink_pair(mesh, mesh_peer);
405         meshlink_set_channel_accept_cb(mesh_peer, accept_cb);
406
407         assert_true(meshlink_start(mesh));
408         assert_true(meshlink_start(mesh_peer));
409         meshlink_node_t *node = meshlink_get_node(mesh, PEER);
410
411         /* 1. Accept and stay idle for 65 seconds */
412
413         bzero(&poll_cb_data, sizeof(poll_cb_data));
414         bzero(&recv_cb_data, sizeof(recv_cb_data));
415
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);
419         sleep(65);
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;
423
424         // Send 7 MSS size packet
425
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));
431
432         size_t mss_size = meshlink_channel_get_mss(mesh, channel);
433         assert_int_not_equal(mss_size, -1);
434
435         if(mss_size * 7 <= default_max_size) {
436                 send_size = mss_size * 7;
437         }
438
439         /* 2. Validate whether poll callback is invoked in case of channel is holding data in send buffer for a while */
440
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);
448
449         /* 3. On sending max send buffer sized packed on a channel should not invoke callback with length 0 */
450
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);
458
459
460         /* 4. Poll callback should not be invoked when the self node is offline and it has data in it's buffer */
461
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);
467         sleep(2);
468         assert_int_equal(meshlink_channel_send(mesh, channel, buffer, 50), 50);
469         sleep(2);
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);
473
474         /* 5. Changing the sendq size should reflect on the poll callback length */
475
476         bzero(&poll_cb_data, sizeof(poll_cb_data));
477         bzero(&recv_cb_data, sizeof(recv_cb_data));
478
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);
486
487         /* If peer node's channel is closed/freed but the host node is not freed then poll callback with length 0 is expected */
488
489         /*assert_int_not_equal(poll_cb_data.cb_total_data_len, 0);
490
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);*/
494
495         // Cleanup
496
497         free(buffer);
498         meshlink_close(mesh);
499         meshlink_close(mesh_peer);
500         assert_true(meshlink_destroy(nut_confbase));
501         assert_true(meshlink_destroy(peer_confbase));
502         return true;
503 }
504
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)
515         };
516         total_tests += sizeof(blackbox_channel_set_poll_cb_tests) / sizeof(blackbox_channel_set_poll_cb_tests[0]);
517
518         return cmocka_run_group_tests(blackbox_channel_set_poll_cb_tests, NULL, NULL);
519 }