]> git.meshlink.io Git - meshlink/commitdiff
Update the blackbox join test cases.
authorGuus Sliepen <guus@meshlink.io>
Sun, 23 Feb 2020 00:38:24 +0000 (01:38 +0100)
committerGuus Sliepen <guus@meshlink.io>
Sun, 23 Feb 2020 00:38:24 +0000 (01:38 +0100)
src/devtools.c
src/devtools.h
src/meshlink.c
src/meshlink.sym
src/protocol_auth.c
test/blackbox/run_blackbox_tests/test_cases_join.c

index a4468f024794508ab199d08faba4590d5a4ecad1..5a49e3a860296a66ad049bd7d238946424ef1b2d 100644 (file)
@@ -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.
index 56b2afa4f3c6830b5afec674ee6f6a9e4bf1a0da..6d0b4989c3ce32c18c124faf8bef9219794ba712 100644 (file)
@@ -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
index 903ff09a2040049d1b979bf8b3689bcb0ee42657..07c518e0dd8c8c112aea2f85a84a756947998005 100644 (file)
@@ -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);
index 4a89b5eac6394c8c2d675c4a2d4723bcc41f825c..de640c0c7069cac8c4e21f7dc11bd54043c34766 100644 (file)
@@ -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
index 1ece41cea592bf6e62d607104447c8bcc15ec0c1..416066ff0ed0a6eb6654bab6a0e70bc2b709e8e6 100644 (file)
@@ -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);
 
index 865528574f6c8bdec4f16087cb0ad76eb4e38a8a..882ec2c738700812fc48fc522bafcdfc0bced25f 100644 (file)
 #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 <assert.h>
 #include <string.h>
 #include <stdlib.h>
 #include <setjmp.h>
 #include <cmocka.h>
 #include <pthread.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utime.h>
+#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;
 }
-