From 18c0a32a4de44adfeee6be42d1c1ae231d093cbf Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 23 Feb 2020 01:38:24 +0100 Subject: [PATCH] Update the blackbox join test cases. --- src/devtools.c | 6 + src/devtools.h | 10 + src/meshlink.c | 4 + src/meshlink.sym | 1 + src/protocol_auth.c | 5 + .../run_blackbox_tests/test_cases_join.c | 826 +++++++++++++++--- 6 files changed, 730 insertions(+), 122 deletions(-) diff --git a/src/devtools.c b/src/devtools.c index a4468f02..5a49e3a8 100644 --- a/src/devtools.c +++ b/src/devtools.c @@ -39,8 +39,14 @@ static void keyrotate_nop_probe(int stage) { return; } +static void inviter_commits_first_nop_probe(bool stage) { + (void)stage; + return; +} + void (*devtool_trybind_probe)(void) = trybind_nop_probe; void (*devtool_keyrotate_probe)(int stage) = keyrotate_nop_probe; +void (*devtool_set_inviter_commits_first)(bool inviter_commited_first) = inviter_commits_first_nop_probe; /* Return an array of edges in the current network graph. * Data captures the current state and will not be updated. diff --git a/src/devtools.h b/src/devtools.h index 56b2afa4..6d0b4989 100644 --- a/src/devtools.h +++ b/src/devtools.h @@ -166,4 +166,14 @@ extern void (*devtool_trybind_probe)(void); */ extern void (*devtool_keyrotate_probe)(int stage); +/// Debug function pointer variable for asserting inviter/invitee committing sequence +/** This function pointer variable is a userspace tracepoint or debugger callback which + * invokes either after inviter writing invitees host file into the disk + * or after invitee writing it's main config file and host config files that inviter sent into + * the disk. + * + * @param inviter_commited_first true if inviter committed first else false if invitee committed first the other host file into the disk. + */ +extern void (*devtool_set_inviter_commits_first)(bool inviter_commited_first); + #endif diff --git a/src/meshlink.c b/src/meshlink.c index 903ff09a..07c518e0 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -743,6 +743,10 @@ static bool finalize_join(join_state_t *state, const void *buf, uint16_t len) { return false; } + if(!mesh->inviter_commits_first) { + devtool_set_inviter_commits_first(false); + } + sptps_send_record(&state->sptps, 1, ecdsa_get_public_key(mesh->private_key), 32); logger(mesh, MESHLINK_DEBUG, "Configuration stored in: %s\n", mesh->confbase); diff --git a/src/meshlink.sym b/src/meshlink.sym index 4a89b5ea..de640c0c 100644 --- a/src/meshlink.sym +++ b/src/meshlink.sym @@ -5,6 +5,7 @@ devtool_get_all_submeshes devtool_get_node_status devtool_keyrotate_probe devtool_open_in_netns +devtool_set_inviter_commits_first devtool_trybind_probe meshlink_add_address meshlink_add_external_address diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 1ece41ce..416066ff 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -21,6 +21,7 @@ #include "conf.h" #include "connection.h" +#include "devtools.h" #include "ecdsa.h" #include "edge.h" #include "graph.h" @@ -232,6 +233,10 @@ static bool process_invitation(meshlink_handle_t *mesh, connection_t *c, const v return false; } + if(mesh->inviter_commits_first) { + devtool_set_inviter_commits_first(true); + } + // Send the node the contents of the invitation file sptps_send_record(&c->sptps, 0, config.buf, config.len); diff --git a/test/blackbox/run_blackbox_tests/test_cases_join.c b/test/blackbox/run_blackbox_tests/test_cases_join.c index 86552857..882ec2c7 100644 --- a/test/blackbox/run_blackbox_tests/test_cases_join.c +++ b/test/blackbox/run_blackbox_tests/test_cases_join.c @@ -21,11 +21,6 @@ #undef NDEBUG #endif -#include "execute_tests.h" -#include "test_cases_join.h" -#include "../common/containers.h" -#include "../common/test_step.h" -#include "../common/common_handlers.h" #include #include #include @@ -33,169 +28,757 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include "execute_tests.h" +#include "test_cases_get_node_reachability.h" +#include "../common/test_step.h" +#include "../common/common_handlers.h" +#include "../../utils.h" +#include "../../../src/devtools.h" + +#define NUT "nut" +#define PEER "peer" +#define PEER2 "peer2" +#define TEST_MESHLINK_JOIN "test_meshlink_join" +#define create_path(confbase, node_name, test_case_no) assert(snprintf(confbase, sizeof(confbase), TEST_MESHLINK_JOIN "_%ld_%s_%02d", (long) getpid(), node_name, test_case_no) > 0) + +static struct sync_flag peer_reachable_status_cond = {.mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER}; +static bool peer_reachable_status; +static struct sync_flag nut_reachable_status_cond = {.mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER}; +static bool nut_reachable_status; +static struct sync_flag nut_started_status_cond = {.mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER}; + +/* Node reachable status callback which signals the respective conditional varibale */ +static void meshlink_node_reachable_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable_status) { + if(!strcasecmp(mesh->name, NUT)) { + if(!strcasecmp(node->name, PEER)) { + peer_reachable_status = reachable_status; + set_sync_flag(&peer_reachable_status_cond, true); + } + } else if(!strcasecmp(mesh->name, PEER)) { + if(!strcasecmp(node->name, NUT)) { + nut_reachable_status = reachable_status; + set_sync_flag(&nut_reachable_status_cond, true); + } + } +} -/* Modify this to change the logging level of Meshlink */ -#define TEST_MESHLINK_LOG_LEVEL MESHLINK_DEBUG +/* SIGUSR2 signal handler that signals the NUT started and PEER node can join */ +static void nut_started_user_signal_handler(int signum) { + if(signum == SIGUSR2) { + set_sync_flag(&nut_started_status_cond, true); + } + +} -static void test_case_meshlink_join_01(void **state); -static bool test_meshlink_join_01(void); -static void test_case_meshlink_join_02(void **state); -static bool test_meshlink_join_02(void); -static void test_case_meshlink_join_03(void **state); -static bool test_meshlink_join_03(void); +/* Test Steps for meshlink_join Test Case # 1 - Valid case -/* State structure for join Test Case #1 */ -static black_box_state_t test_case_join_01_state = { - .test_case_name = "test_case_join_01", -}; + Test Steps: + 1. Open instances for NUT and peer, peer invites NUT and starts instance. + 2. NUT consumes the invitation generated by peer -/* State structure for join Test Case #1 */ -static black_box_state_t test_case_join_02_state = { - .test_case_name = "test_case_join_02", -}; + Expected Result: + NUT joins peer using the invitation generated. +*/ +static void test_case_meshlink_join_01(void **state) { + (void) state; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 1); + create_path(peer_confbase, PEER, 1); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open both NUT and peer node instance, invite and join NUT with peer node. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + meshlink_set_inviter_commits_first(mesh_peer, true); + meshlink_set_node_status_cb(mesh_peer, meshlink_node_reachable_status_cb); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + assert_true(meshlink_start(mesh_peer)); + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh); + meshlink_set_inviter_commits_first(mesh, true); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + assert_true(meshlink_join(mesh, invitation)); + free(invitation); -/* State structure for join Test Case #1 */ -static black_box_state_t test_case_join_03_state = { - .test_case_name = "test_case_join_03", -}; + meshlink_node_t *peer_handle = meshlink_get_node(mesh, PEER); + assert_non_null(peer_handle); + meshlink_node_t *nut_handle = meshlink_get_node(mesh_peer, NUT); + assert_non_null(nut_handle); -static bool join_status; + // Bring nodes online. -/* status callback */ -static void status_callback(meshlink_handle_t *mesh, meshlink_node_t *source, bool reach) { - (void)mesh; + set_sync_flag(&peer_reachable_status_cond, false); + set_sync_flag(&nut_reachable_status_cond, false); + assert_true(meshlink_start(mesh)); + assert_true(wait_sync_flag(&peer_reachable_status_cond, 60)); + assert_true(peer_reachable_status); + assert_true(wait_sync_flag(&nut_reachable_status_cond, 60)); + assert_true(nut_reachable_status); - if(!strcmp(source->name, "relay")) { - join_status = reach; - } -} + // Cleanup -/* Execute join Test Case # 1 - valid case*/ -static void test_case_meshlink_join_01(void **state) { - execute_test(test_meshlink_join_01, state); + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; } -/* Test Steps for meshlink_join Test Case # 1 - Valid case +/* Test Steps for meshlink_join Test Case # 2 - Invalid case Test Steps: - 1. Generate invite in relay container and run 'relay' node - 2. Run NUT - 3. Join NUT with relay using invitation generated. + 1. Call meshlink_join with NULL as mesh handler or node name argument. Expected Result: - NUT joins relay using the invitation generated. + NUT joining fails when NULL is passed as mesh handle or node name argument */ -static bool test_meshlink_join_01(void) { - assert(meshlink_destroy("join_conf.1")); - assert(meshlink_destroy("join_conf.2")); - - // Create node instances - meshlink_handle_t *mesh1 = meshlink_open("join_conf.1", "nut", "test", DEV_CLASS_STATIONARY); - assert(mesh1 != NULL); - meshlink_set_log_cb(mesh1, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger); - meshlink_handle_t *mesh2 = meshlink_open("join_conf.2", "relay", "test", DEV_CLASS_STATIONARY); - assert(mesh2 != NULL); - meshlink_set_log_cb(mesh2, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger); - - // Setting node status callback - meshlink_set_node_status_cb(mesh1, status_callback); - - // Inviting nut - assert(meshlink_start(mesh2)); - char *invitation = meshlink_invite(mesh2, NULL, "nut"); - assert(invitation); - - // Joining Node-Under-Test with relay - bool ret = meshlink_join(mesh1, invitation); - assert_int_equal(ret, true); - assert(meshlink_start(mesh1)); - sleep(1); - - assert_int_equal(join_status, true); +static void test_case_meshlink_join_02(void **state) { + (void) state; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 2); + create_path(peer_confbase, PEER, 2); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open both NUT and peer node instance, invite and join NUT with peer node. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + meshlink_set_node_status_cb(mesh_peer, meshlink_node_reachable_status_cb); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + assert_true(meshlink_start(mesh_peer)); + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + + // meshlink_join called with NULL as mesh handle and with valid invitation + + assert_int_equal(meshlink_join(NULL, invitation), false); + assert_int_equal(meshlink_join(mesh, NULL), false); + + // Cleanup free(invitation); - meshlink_close(mesh1); - meshlink_close(mesh2); - assert(meshlink_destroy("join_conf.1")); - assert(meshlink_destroy("join_conf.2")); + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; +} + +/* Test Steps for meshlink_join Test Case # 3 - Persistence testing around inviter + + Test steps and scenarios: + 1. Open Node-Under-Test (NUT) and invite peer node and close it's instance. + Spawn a process which waits for the peer node to join and raises SIGINT if the + appropriate callback is received (on the other hand the test suite opens and joins + the peer node with NUT in the forked process). + Expected Result: + NUT joins peer successfully + + +*/ +static void test_case_meshlink_join_03(void **state) { + (void) state; + pid_t pid; + int pid_status; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 3); + create_path(peer_confbase, PEER, 3); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open NUT node instance and invite peer node. Close NUT node instance. + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert_non_null(mesh); + char *invitation = meshlink_invite(mesh, NULL, PEER); + meshlink_close(mesh); + + // Set the SIGUSR2 signal handler with handler that signal the condition to the test suite + + sighandler_t usr2sighandler = signal(SIGUSR2, nut_started_user_signal_handler); + assert_int_not_equal(usr2sighandler, SIG_ERR); + + // Fork a new process and run NUT in it which just waits for the peer node reachable status callback + // and terminates the process immediately. + + pid = fork(); + assert_int_not_equal(pid, -1); + + if(!pid) { + assert(signal(SIGUSR2, SIG_DFL) != SIG_ERR); - return true; + mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert(mesh); + meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + + set_sync_flag(&peer_reachable_status_cond, false); + assert(meshlink_start(mesh)); + + assert(kill(getppid(), SIGUSR2) != -1); + + assert(wait_sync_flag(&peer_reachable_status_cond, 60)); + assert(peer_reachable_status); + + raise(SIGINT); + } + + // Open peer node instance and join with the invitation obtained. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + + // Wait for the started signal from NUT and reset the previous SIGUSR2 signal handler + + assert_true(wait_sync_flag(&nut_started_status_cond, 60)); + assert_int_not_equal(signal(SIGUSR2, usr2sighandler), SIG_ERR); + + assert_true(meshlink_join(mesh_peer, invitation)); + assert_true(meshlink_start(mesh_peer)); + + // Wait for child exit and verify which signal terminated it + + assert_int_not_equal(waitpid(pid, &pid_status, 0), -1); + assert_int_equal(WIFSIGNALED(pid_status), true); + assert_int_equal(WTERMSIG(pid_status), SIGINT); + + // Reopen the NUT instance in the same test suite + + mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert_non_null(mesh); + + assert_non_null(meshlink_get_node(mesh, PEER)); + + // Cleanup + + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; } -/* Execute join Test Case # 2 - Invalid case*/ -static void test_case_meshlink_join_02(void **state) { - execute_test(test_meshlink_join_02, state); +/* Test Steps for meshlink_get_node_reachability Test Case # 4 - Persistence testing around invitee + + Test steps and scenarios: + 1. Open peer node instance, invite NUT and start peer node. Spawn a new process in + which it opens and joins the NUT with peer node. + Reopen NUT instance in the test suite process and verify peer is joined. + Expected Result: + NUT joins peer successfully + +*/ +static void test_case_meshlink_join_04(void **state) { + (void) state; + pid_t pid; + int pid_status; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 4); + create_path(peer_confbase, PEER, 4); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open peer node instance and invite NUT. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_int_not_equal(mesh_peer, NULL); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + + assert_true(meshlink_start(mesh_peer)); + + // Fork a new process in which NUT is joins with the peer node and raises SIGINT to terminate. + + pid = fork(); + assert_int_not_equal(pid, -1); + + if(!pid) { + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert(mesh); + meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb); + + assert(meshlink_join(mesh, invitation)); + + raise(SIGINT); + } + + // Wait for child exit and verify which signal terminated it + + assert_int_not_equal(waitpid(pid, &pid_status, 0), -1); + assert_int_equal(WIFSIGNALED(pid_status), true); + assert_int_equal(WTERMSIG(pid_status), SIGINT); + + // Reopen the NUT instance in the same test suite + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert_non_null(mesh); + + assert_non_null(meshlink_get_node(mesh, PEER)); + + // Cleanup + + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; } -/* Test Steps for meshlink_join Test Case # 2 - Invalid case +static void nop_stage(bool stage) { + (void)stage; + return; +} + +static void debug_probe(bool stage) { + (void)stage; + raise(SIGINT); + return; +} + +/* Test Steps for meshlink_get_node_reachability Test Case # 5 - Test the invitee committing first scenario + + Test steps and scenarios: + 1. Open peer node instance, invite NUT and start peer node. Enable the debug probe, Spawn a new process in + which it opens and joins the NUT with peer node which terminates the NUT while joining. + Reopen NUT instance in the test suite process and verify peer is joined. + Expected Result: + NUT(invitee) commits the config file(s) first but peer is unaware of NUT. + +*/ +static void test_case_meshlink_join_05(void **state) { + (void) state; + pid_t pid; + int pid_status; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 5); + create_path(peer_confbase, PEER, 5); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + assert(signal(SIGINT, SIG_DFL) != SIG_ERR); + assert(signal(SIGABRT, SIG_DFL) != SIG_ERR); + + // Set debug_probe callback + + devtool_set_inviter_commits_first = debug_probe; + + // Open peer node instance and invite NUT. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_int_not_equal(mesh_peer, NULL); + meshlink_set_inviter_commits_first(mesh_peer, false); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + + assert_true(meshlink_start(mesh_peer)); + + // Fork a new process in which NUT is joins with the peer node and raises SIGINT to terminate. + + pid = fork(); + assert_int_not_equal(pid, -1); + + if(!pid) { + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert(mesh); + meshlink_set_inviter_commits_first(mesh_peer, false); + meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb); + + assert_true(meshlink_join(mesh, invitation)); + + raise(SIGABRT); + } + + // Wait for child exit and verify which signal terminated it + printf("\n"); + assert_int_not_equal(waitpid(pid, &pid_status, 0), -1); + assert_int_equal(WIFSIGNALED(pid_status), true); + assert_int_equal(WTERMSIG(pid_status), SIGINT); + + // Reopen the NUT instance in the same test suite + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert_non_null(mesh); + + // Invitee committed host config file but invitee should not + + assert_non_null(meshlink_get_node(mesh, PEER)); + assert_null(meshlink_get_node(mesh_peer, NUT)); + + // Cleanup + + free(invitation); + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + + devtool_set_inviter_commits_first = nop_stage; + return; +} + +/* Test Steps for meshlink_get_node_reachability Test Case # 6 - Test the inviter committing first scenario + + Test steps and scenarios: + 1. Open NUT node instance, invite peer and close the instance. Enable the debug probe, Spawn a new process in + which it starts the NUT instance. At the parents/test vector thread wait for the signal that NUT raises after starting + and join peer with NUT. NUT terminates in debug probe after committing into the disk + Reopen NUT instance in the test suite process and verify peer is joined. + Expected Result: + NUT(inviter) commits the config file(s) first but peer is unaware of NUT. + +*/ +static void test_case_meshlink_join_06(void **state) { + (void) state; + pid_t pid; + int pid_status; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 6); + create_path(peer_confbase, PEER, 6); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + assert(signal(SIGINT, SIG_DFL) != SIG_ERR); + assert(signal(SIGABRT, SIG_DFL) != SIG_ERR); + + // Set debug_probe callback + + devtool_set_inviter_commits_first = debug_probe; + + // Open NUT node instance and invite peer node. Close NUT node instance. + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert_non_null(mesh); + meshlink_set_inviter_commits_first(mesh, true); + char *invitation = meshlink_invite(mesh, NULL, PEER); + meshlink_close(mesh); + + // Set the SIGUSR2 signal handler with handler that signal the condition to the test suite + + sighandler_t usr2sighandler = signal(SIGUSR2, nut_started_user_signal_handler); + assert_int_not_equal(usr2sighandler, SIG_ERR); + set_sync_flag(&peer_reachable_status_cond, false); + + // Fork a new process and run NUT in it which just waits for the peer node reachable status callback + // and terminates the process immediately. + + pid = fork(); + assert_int_not_equal(pid, -1); + + if(!pid) { + assert(signal(SIGUSR2, SIG_DFL) != SIG_ERR); + + mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert(mesh); + meshlink_set_inviter_commits_first(mesh, true); + meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + + assert(meshlink_start(mesh)); + + assert(kill(getppid(), SIGUSR2) != -1); + + sleep(10); + + raise(SIGABRT); + } + + // Open peer node instance and join with the invitation obtained. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + meshlink_set_inviter_commits_first(mesh_peer, true); + + // Wait for the started signal from NUT and reset the previous SIGUSR2 signal handler + + assert_true(wait_sync_flag(&nut_started_status_cond, 60)); + assert_int_not_equal(signal(SIGUSR2, usr2sighandler), SIG_ERR); + + assert_false(meshlink_join(mesh_peer, invitation)); + + // Wait for child exit and verify which signal terminated it + + assert_int_not_equal(waitpid(pid, &pid_status, 0), -1); + assert_int_equal(WIFSIGNALED(pid_status), true); + assert_int_equal(WTERMSIG(pid_status), SIGINT); + + // Reopen the NUT instance in the same test suite + + mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, DEV_CLASS_STATIONARY); + assert_non_null(mesh); + + // Inviter should first commit config file(s) into the disk + + assert_null(meshlink_get_node(mesh_peer, NUT)); + assert_non_null(meshlink_get_node(mesh, PEER)); + + // Cleanup + + free(invitation); + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + devtool_set_inviter_commits_first = nop_stage; + return; +} + +/* Test Steps for meshlink_join Test Case # 7 - Inviter sets that invitee should commit first, + even invitee sets that inviter should commit first. Test Steps: - 1. Call meshlink_join with NULL as mesh handler argument. + 1. Open instances for NUT and peer, peer invites NUT and starts instance. + Both the instances sets meshlink_set_inviter_commits_first API mutually exclusively + NUT tries to consume the invitation generated by peer Expected Result: - report error accordingly when NULL is passed as mesh handle argument + NUT fails to join peer using the invitation generated. */ -static bool test_meshlink_join_02(void) { - assert(meshlink_destroy("join_conf.3")); +static void test_case_meshlink_join_07(void **state) { + (void) state; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 7); + create_path(peer_confbase, PEER, 7); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open both NUT and peer node instance, invite and join NUT with peer node. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + meshlink_set_inviter_commits_first(mesh_peer, false); + meshlink_set_node_status_cb(mesh_peer, meshlink_node_reachable_status_cb); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + assert_true(meshlink_start(mesh_peer)); + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh); + meshlink_set_inviter_commits_first(mesh, true); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + assert_false(meshlink_join(mesh, invitation)); + free(invitation); + + meshlink_node_t *peer_handle = meshlink_get_node(mesh, PEER); + assert_null(peer_handle); + meshlink_node_t *nut_handle = meshlink_get_node(mesh_peer, NUT); + assert_null(nut_handle); + + // Cleanup - // Create node instances - meshlink_handle_t *mesh1 = meshlink_open("join_conf.3", "nut", "test", DEV_CLASS_STATIONARY); - assert(mesh1 != NULL); - meshlink_set_log_cb(mesh1, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger); + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; +} - char *invitation = meshlink_invite(mesh1, NULL, "nodex"); +/* Test Steps for meshlink_join Test Case # 8 - Inviter sets that it should commit first, + even invitee sets that it should commit first - /* meshlink_join called with NULL as mesh handle and with valid invitation */ - bool ret = meshlink_join(NULL, invitation); - assert_int_equal(ret, false); + Test Steps: + 1. Open instances for NUT and peer, peer invites NUT and starts instance. + Both the instances sets meshlink_set_inviter_commits_first API mutually exclusively + NUT tries to consume the invitation generated by peer + Expected Result: + NUT fails to join peer using the invitation generated. +*/ +static void test_case_meshlink_join_08(void **state) { + (void) state; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 8); + create_path(peer_confbase, PEER, 8); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open both NUT and peer node instance, invite and join NUT with peer node. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + meshlink_set_inviter_commits_first(mesh_peer, true); + meshlink_set_node_status_cb(mesh_peer, meshlink_node_reachable_status_cb); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + assert_true(meshlink_start(mesh_peer)); + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh); + meshlink_set_inviter_commits_first(mesh, false); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + assert_false(meshlink_join(mesh, invitation)); free(invitation); - meshlink_close(mesh1); - assert(meshlink_destroy("join_conf.3")); - return true; + meshlink_node_t *peer_handle = meshlink_get_node(mesh, PEER); + assert_null(peer_handle); + meshlink_node_t *nut_handle = meshlink_get_node(mesh_peer, NUT); + assert_null(nut_handle); + + // Cleanup + + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; } -/* Execute join Test Case # 3- Invalid case*/ -static void test_case_meshlink_join_03(void **state) { - execute_test(test_meshlink_join_03, state); +/* Test Steps for meshlink_join Test Case # 9 - Invitee already started its instance + + Test Steps: + 1. Open instances for NUT and peer, peer invites NUT and both the instances starts their instances. + NUT tries to join the peer with the generated invitation. + + Expected Result: + NUT fails to join peer using the invitation generated. +*/ +static void test_case_meshlink_join_09(void **state) { + (void) state; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + create_path(nut_confbase, NUT, 9); + create_path(peer_confbase, PEER, 9); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open both NUT and peer node instance, invite and join NUT with peer node. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + meshlink_set_node_status_cb(mesh_peer, meshlink_node_reachable_status_cb); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + assert_true(meshlink_start(mesh_peer)); + + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + + assert_true(meshlink_start(mesh)); + + assert_false(meshlink_join(mesh, invitation)); + free(invitation); + + meshlink_node_t *peer_handle = meshlink_get_node(mesh, PEER); + assert_null(peer_handle); + meshlink_node_t *nut_handle = meshlink_get_node(mesh_peer, NUT); + assert_null(nut_handle); + + // Cleanup + + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; } -/* Test Steps for meshlink_join Test Case # 3 - Invalid case +/* Test Steps for meshlink_join Test Case # 10 - Invitee already joined in a mesh Test Steps: - 1. Run NUT - 1. Call meshlink_join with NULL as invitation argument. + 1. Open instances for NUT, peer2 and peer, peer invites NUT. Peer2 and NUT both mutually imports data + i.e, both formed or joined the mesh. + NUT tries to join the peer with the generated invitation. Expected Result: - Report error accordingly when NULL is passed as invite argument + NUT fails to join peer using the invitation generated. */ -static bool test_meshlink_join_03(void) { - assert(meshlink_destroy("joinconf.4")); - meshlink_set_log_cb(NULL, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger); - - /* Create meshlink instance */ - mesh_handle = meshlink_open("joinconf.4", "nut", "node_sim", 1); - assert(mesh_handle); - meshlink_set_log_cb(mesh_handle, TEST_MESHLINK_LOG_LEVEL, meshlink_callback_logger); - - /* Passing NULL as invitation to join API*/ - bool ret = meshlink_join(mesh_handle, NULL); - assert_int_equal(ret, false); - - meshlink_close(mesh_handle); - assert(meshlink_destroy("joinconf.4")); - return true; +static void test_case_meshlink_join_10(void **state) { + (void) state; + char nut_confbase[PATH_MAX]; + char peer_confbase[PATH_MAX]; + char peer_confbase2[PATH_MAX]; + create_path(nut_confbase, NUT, 10); + create_path(peer_confbase, PEER, 10); + create_path(peer_confbase2, PEER2, 10); + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + // Open both NUT and peer node instance, invite and join NUT with peer node. + + meshlink_handle_t *mesh_peer = meshlink_open(peer_confbase, PEER, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer); + meshlink_set_node_status_cb(mesh_peer, meshlink_node_reachable_status_cb); + char *invitation = meshlink_invite(mesh_peer, NULL, NUT); + assert_non_null(invitation); + assert_true(meshlink_start(mesh_peer)); + + meshlink_handle_t *mesh_peer2 = meshlink_open(peer_confbase2, PEER2, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh_peer2); + meshlink_handle_t *mesh = meshlink_open(nut_confbase, NUT, TEST_MESHLINK_JOIN, + DEV_CLASS_STATIONARY); + assert_non_null(mesh); + meshlink_set_node_status_cb(mesh, meshlink_node_reachable_status_cb); + + char *data = meshlink_export(mesh); + assert_non_null(data); + assert_true(meshlink_import(mesh_peer2, data)); + free(data); + data = meshlink_export(mesh_peer2); + assert_non_null(data); + assert_true(meshlink_import(mesh, data)); + free(data); + + assert_true(meshlink_start(mesh)); + + assert_false(meshlink_join(mesh, invitation)); + free(invitation); + + meshlink_node_t *peer_handle = meshlink_get_node(mesh, PEER); + assert_null(peer_handle); + meshlink_node_t *nut_handle = meshlink_get_node(mesh_peer, NUT); + assert_null(nut_handle); + + // Cleanup + + meshlink_close(mesh); + meshlink_close(mesh_peer); + assert_true(meshlink_destroy(nut_confbase)); + assert_true(meshlink_destroy(peer_confbase)); + return; } int test_meshlink_join(void) { const struct CMUnitTest blackbox_join_tests[] = { - cmocka_unit_test_prestate_setup_teardown(test_case_meshlink_join_01, NULL, NULL, - (void *)&test_case_join_01_state), - cmocka_unit_test_prestate_setup_teardown(test_case_meshlink_join_02, NULL, NULL, - (void *)&test_case_join_02_state), - cmocka_unit_test_prestate_setup_teardown(test_case_meshlink_join_03, NULL, NULL, - (void *)&test_case_join_03_state) + cmocka_unit_test(test_case_meshlink_join_01), + cmocka_unit_test(test_case_meshlink_join_02), + cmocka_unit_test(test_case_meshlink_join_03), + cmocka_unit_test(test_case_meshlink_join_04), + cmocka_unit_test(test_case_meshlink_join_05), + cmocka_unit_test(test_case_meshlink_join_06), + cmocka_unit_test(test_case_meshlink_join_07), + cmocka_unit_test(test_case_meshlink_join_08), + cmocka_unit_test(test_case_meshlink_join_09), + cmocka_unit_test(test_case_meshlink_join_10) }; total_tests += sizeof(blackbox_join_tests) / sizeof(blackbox_join_tests[0]); @@ -203,4 +786,3 @@ int test_meshlink_join(void) { return failed; } - -- 2.39.2