]> git.meshlink.io Git - meshlink/commitdiff
Add blackbox test cases for submesh
authorLakshminarayana Gurram <38289425+lakshminarayanagurram@users.noreply.github.com>
Tue, 5 Mar 2019 12:19:48 +0000 (17:49 +0530)
committerGuus Sliepen <guus@meshlink.io>
Sun, 17 Mar 2019 20:16:50 +0000 (21:16 +0100)
45 files changed:
configure.ac
test/blackbox/Makefile.am
test/blackbox/common/containers.c
test/blackbox/common/mesh_event_handler.c
test/blackbox/common/mesh_event_handler.h
test/blackbox/common/test_step.c
test/blackbox/common/test_step.h
test/blackbox/run_blackbox_tests/Makefile.am
test/blackbox/run_blackbox_tests/execute_tests.c
test/blackbox/run_blackbox_tests/execute_tests.h
test/blackbox/run_blackbox_tests/run_blackbox_tests.c
test/blackbox/run_blackbox_tests/test_cases_submesh01.c [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_submesh01.h [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_submesh02.c [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_submesh02.h [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_submesh03.c [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_submesh03.h [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_submesh04.c [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_submesh04.h [new file with mode: 0644]
test/blackbox/test_case_channel_conn_01/Makefile.am
test/blackbox/test_case_meta_conn_04/node_sim_nut.c
test/blackbox/test_case_meta_conn_05/node_sim_nut.c
test/blackbox/test_cases_submesh01/Makefile.am [new file with mode: 0644]
test/blackbox/test_cases_submesh01/node_sim_app1node1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh01/node_sim_app1node2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh01/node_sim_app2node1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh01/node_sim_app2node2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh01/node_sim_corenode1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh01/node_sim_corenode2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh02/Makefile.am [new file with mode: 0644]
test/blackbox/test_cases_submesh02/node_sim_app1node1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh02/node_sim_app1node2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh02/node_sim_app2node1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh02/node_sim_app2node2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh02/node_sim_corenode1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh02/node_sim_corenode2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh03/Makefile.am [new file with mode: 0644]
test/blackbox/test_cases_submesh03/node_sim_app1node1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh03/node_sim_app1node2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh03/node_sim_corenode1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh04/Makefile.am [new file with mode: 0644]
test/blackbox/test_cases_submesh04/node_sim_app1node1.c [new file with mode: 0644]
test/blackbox/test_cases_submesh04/node_sim_app1node2.c [new file with mode: 0644]
test/blackbox/test_cases_submesh04/node_sim_corenode1.c [new file with mode: 0644]
test/blackbox/util/gen_invite.c

index 2309d358e2920ae90aaec8709860647d4e299271..c1baec17d7f37c9a7f659e026db0188bde493121 100644 (file)
@@ -167,6 +167,10 @@ AC_CONFIG_FILES([
         test/blackbox/test_case_meta_conn_03/Makefile
         test/blackbox/test_case_meta_conn_04/Makefile
         test/blackbox/test_case_meta_conn_05/Makefile
+        test/blackbox/test_cases_submesh01/Makefile
+        test/blackbox/test_cases_submesh02/Makefile
+        test/blackbox/test_cases_submesh03/Makefile
+        test/blackbox/test_cases_submesh04/Makefile
         examples/Makefile
 ])
 
index 5f1c6430e53504a41442ef849bfa2141882f772e..df5792bdb3c9de3bb6c8264de4b3e1c442cdcab2 100644 (file)
@@ -13,7 +13,12 @@ SUBDIRS = \
        test_case_meta_conn_02 \
        test_case_meta_conn_03 \
        test_case_meta_conn_04 \
-       test_case_meta_conn_05
+       test_case_meta_conn_05 \
+       test_cases_submesh01 \
+       test_cases_submesh02 \
+       test_cases_submesh03 \
+       test_cases_submesh04
+
 
 gen_invite_SOURCES = util/gen_invite.c common/common_handlers.c common/test_step.c common/mesh_event_handler.c
 gen_invite_LDADD = ../../src/libmeshlink.la
index 6d27151c051d871137f8fe167ce886424decd944..99cdbdfed2e7a7ee18d52256e73966ff811603f0 100644 (file)
@@ -70,7 +70,7 @@ void rename_container(const char *old_name, const char *new_name) {
 }
 
 char *run_in_container(const char *cmd, const char *container_name, bool daemonize) {
-       char container_find_name[100];
+       char container_find_name[1000];
        struct lxc_container *container;
 
        assert(cmd);
@@ -377,6 +377,23 @@ char *invite_in_container(const char *inviter, const char *invitee) {
        return invite_url;
 }
 
+/* Run the gen_invite command inside the 'inviter' container to generate an invite
+    for 'invitee' belonging to pparticular submesh, and return the generated invite
+    which is output on the terminal */
+char *submesh_invite_in_container(const char *inviter, const char *invitee, const char *submesh) {
+       char invite_command[200];
+       char *invite_url;
+
+       assert(snprintf(invite_command, sizeof(invite_command),
+                       "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/gen_invite %s %s %s "
+                       "2> gen_invite.log", inviter, invitee, submesh) >= 0);
+       assert(invite_url = run_in_container(invite_command, inviter, false));
+       PRINT_TEST_CASE_MSG("Invite Generated from '%s' to '%s': %s\n", inviter,
+                           invitee, invite_url);
+
+       return invite_url;
+}
+
 /* Run the node_sim_<nodename> program inside the 'node''s container */
 void node_sim_in_container(const char *node, const char *device_class, const char *invite_url) {
        char *node_sim_command;
@@ -419,7 +436,7 @@ void node_sim_in_container_event(const char *node, const char *device_class,
 /* Run the node_step.sh script inside the 'node''s container to send the 'sig' signal to the
     node_sim program in the container */
 void node_step_in_container(const char *node, const char *sig) {
-       char node_step_command[200];
+       char node_step_command[1000];
 
        assert(snprintf(node_step_command, sizeof(node_step_command),
                        "/home/ubuntu/test/node_step.sh lt-node_sim_%s %s 1>&2 2> node_step.log",
index 1b7ebe92aec61dd15eee952320313591d7b9f33c..bb5faf1c173c463698266cc7ba339dddd528a138 100644 (file)
 #define SERVER_LISTEN_PORT "9000" /* Port number that is binded with mesh event server socket */
 #define UDP_BUFF_MAX 2000
 
+const char *event_status[] = {
+       [NODE_STARTED]                          = "Node Started",
+       [NODE_JOINED]                           = "Node Joined",
+       [ERR_NETWORK]                           = "Network Error",
+       [CHANNEL_OPENED]                        = "Channel Opened",
+       [CHANNEL_DATA_RECIEVED]                         = "Channel Data Received",
+       [SIG_ABORT]                             = "SIG_ABORT Received",
+       [MESH_EVENT_COMPLETED]                          = "MESH_EVENT_COMPLETED Received"
+};
+
 // TODO: Implement mesh event handling with reentrancy .
 static struct sockaddr_in server_addr;
 static int client_fd = -1;
@@ -269,5 +279,4 @@ void mesh_event_destroy(void) {
        mesh_events_flush();
        event_receive_thread_running = false;
        pthread_cancel(event_receive_thread);
-}
-
+}
\ No newline at end of file
index 7e497a35682762b30a072c10016a07ee4f8cdd91..e21ef508393381c2d6ce9147b7acc56f31afc76f 100644 (file)
@@ -72,10 +72,12 @@ typedef enum {
        INCOMING_META_CONN,
        OUTGOING_META_CONN,
        AUTO_DISCONN,
-
+       MESH_EVENT_COMPLETED,
        MAX_EVENT           // Maximum event enum
 } mesh_event_t;
 
+extern const char *event_status[];
+
 /// mesh event UDP packet
 typedef struct  mesh_event_payload {
        void          *payload;
index fbaab8973ffd238fa5fbab6c204f4eb8eb42aa85..be0416d9121817af2dac734ada41bae374e73432 100644 (file)
@@ -51,8 +51,8 @@ meshlink_handle_t *execute_open(char *node_name, char *dev_class) {
        return mesh_handle;
 }
 
-char *execute_invite(char *invitee) {
-       char *invite_url = meshlink_invite_ex(mesh_handle, NULL, invitee, MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_NUMERIC);
+char *execute_invite(char *invitee, meshlink_submesh_t *submesh) {
+       char *invite_url = meshlink_invite_ex(mesh_handle, submesh, invitee, MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_NUMERIC);
 
        PRINT_TEST_CASE_MSG("meshlink_invite status: %s\n", meshlink_strerror(meshlink_errno));
        assert(invite_url);
index 7a32dff013f08166cebfe40f9e07ca85ed387a06..1704d346d3394d11c717aa0d509f47c129cf9e9f 100644 (file)
@@ -23,7 +23,7 @@
 #include "../../../src/meshlink.h"
 
 meshlink_handle_t *execute_open(char *node_name, char *dev_class);
-char *execute_invite(char *invitee);
+char *execute_invite(char *invitee, meshlink_submesh_t *submesh);
 void execute_join(char *invite_url);
 void execute_start(void);
 void execute_stop(void);
index 7fe2322521483614f8e9a8d1340d9d5a2928ca90..00aa9b896140bb4d7bdf9ca8659090f1668fe0d7 100644 (file)
@@ -56,7 +56,11 @@ run_blackbox_tests_SOURCES = \
        ../test_case_channel_blacklist_01/node_sim_nut_01.c \
        ../test_case_channel_blacklist_01/node_sim_peer_01.c \
        ../test_case_channel_blacklist_01/node_sim_relay_01.c \
-       test_cases_channel_blacklist.c
+       test_cases_channel_blacklist.c \
+       test_cases_submesh01.c \
+       test_cases_submesh02.c \
+       test_cases_submesh03.c \
+       test_cases_submesh04.c
 
 run_blackbox_tests_LDADD = ../../../src/libmeshlink.la $(LXC_LIBS) $(CMOCKA_LIBS)
 run_blackbox_tests_CFLAGS = -D_GNU_SOURCE $(LXC_CFLAGS) $(CMOCKA_CFLAGS)
index 2f9730e13dc76e3291626e839b27fdc643be2d34..7232911ed36c705172cf268c59ddb740bdc8fb12 100644 (file)
@@ -79,3 +79,48 @@ int teardown_test(void **state) {
 
        return EXIT_SUCCESS;
 }
+
+bool change_state(node_status_t *status, mesh_event_t currentEv) {
+
+       if(status->current_index == status->max_events) {
+               return false;
+       }
+
+       if(currentEv == status->expected_events[status->current_index]) {
+               status->current_index = status->current_index + 1;
+
+               return true;
+       } else {
+               return false;
+       }
+}
+
+void signal_node_start(node_status_t *node_status, int start, int end, char *node_ids[]) {
+       int i, index;
+
+       for(i = start; i <= end; i++) {
+               index = node_status[i].current_index;
+
+               if(index < 1 || NODE_JOINED != node_status[i].expected_events[index - 1]) {
+                       return;
+               }
+       }
+
+
+       for(i = start; i <= end; i++) {
+               fprintf(stderr, "\tSending signals to '%s'\n", node_ids[i]);
+               node_step_in_container(node_ids[i], "SIGIO");
+       }
+
+       return;
+}
+
+bool check_nodes_finished(node_status_t *node_status, int length) {
+       for(int i = 0; i < length; i++) {
+               if(node_status[i].current_index != node_status[i].max_events) {
+                       return false;
+               }
+       }
+
+       return true;
+}
index 03880f5345d1eaa798c3d47b3a3a3c2dbb684ef3..bdf28ea52bc93f56eff9093cda92a5fd580df5f1 100644 (file)
 */
 
 #include <stdbool.h>
+#include "../common/mesh_event_handler.h"
+
+typedef struct {
+       const mesh_event_t              *expected_events;
+       int                             current_index;
+       int                             max_events;
+} node_status_t;
 
 typedef bool (*test_step_func_t)(void);
 
@@ -28,4 +35,36 @@ int setup_test(void **state);
 void execute_test(test_step_func_t step_func, void **state);
 int teardown_test(void **state);
 
+/// Changes the state of the node state machine.
+/** This function changes the current state of the node
+ *
+ *  @param status           Pointer to status handle of that node.
+ *  @param currentEv        Current event triggered by the node.
+ *
+ *  @return                 This function returns true if state change is successful else returns false
+ */
+extern bool change_state(node_status_t *status, mesh_event_t currentEv);
+
+/// Sends SIGIO signal to all the nodes in the container.
+/** This function Triggers SIGIO signal to all the target applications running inside the container
+ *
+ *  @param status           Pointer to array of status handles of target nodes.
+ *  @param start            Starting index from which to start in the array.
+ *  @param end              Ending index of the array
+ *  @param node_ids         Pointer to array of node id strings
+ *
+ *  @return                 Void
+ */
+extern void signal_node_start(node_status_t *node_status, int start, int end, char *node_ids[]);
+
+/// Checks for the completion of nodes state machines.
+/** This function checks wheather the nodes state machines have reached their maximum state indexes
+ *
+ *  @param status           Pointer to array of status handles of target nodes.
+ *  @param length               Number of nodes to check.
+ *
+ *  @return                 This function returns true if all the nodes reached their max states
+ */
+extern bool check_nodes_finished(node_status_t *node_status, int length);
+
 #endif // TEST_STEP_H
index c5b5c59e72ca685d847b72d01f25a19394dab840..df23d5ae30d452460fcacc6932fa8b2684d8eac5 100644 (file)
 
 #include "test_cases_channel_conn.h"
 #include "test_cases_get_all_nodes_by_dev_class.h"
+#include "test_cases_submesh01.h"
+#include "test_cases_submesh02.h"
+#include "test_cases_submesh03.h"
+#include "test_cases_submesh04.h"
 
 #include "../common/containers.h"
 #include "../common/common_handlers.h"
@@ -85,7 +89,8 @@ int main(int argc, char *argv[]) {
 
        int failed_tests = 0;
 
-       /*failed_tests += test_meta_conn();
+       /*
+       failed_tests += test_meta_conn();
        failed_tests += test_meshlink_set_status_cb();
        failed_tests += test_meshlink_join();
        failed_tests += test_meshlink_set_channel_poll_cb();
@@ -126,8 +131,13 @@ int main(int argc, char *argv[]) {
        failed_tests += test_meshlink_channel_open();
        failed_tests += test_meshlink_channel_close();
 
-       failed_tests += test_meshlink_channel_conn();*/
+       failed_tests += test_meshlink_channel_conn();
        failed_tests += test_optimal_pmtu();
+       */
+       failed_tests += test_cases_submesh01();
+       failed_tests += test_cases_submesh02();
+       failed_tests += test_cases_submesh03();
+       failed_tests += test_cases_submesh04();
 
        failed_tests += test_optimal_pmtu();
 
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh01.c b/test/blackbox/run_blackbox_tests/test_cases_submesh01.c
new file mode 100644 (file)
index 0000000..00f4152
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+    test_cases_submesh.c -- Execution of specific meshlink black box test cases
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <assert.h>
+#include "execute_tests.h"
+#include "test_cases_submesh01.h"
+#include "pthread.h"
+#include "../common/containers.h"
+#include "../common/test_step.h"
+#include "../common/common_handlers.h"
+#include "../common/mesh_event_handler.h"
+
+#define CORENODE1_ID  "0"
+#define APP1NODE1_ID  "1"
+#define APP2NODE1_ID  "2"
+#define CORENODE2_ID  "3"
+#define APP1NODE2_ID  "4"
+#define APP2NODE2_ID  "5"
+
+#define INIT_ST         0
+
+static bool test_case_status = false;
+
+static void test_case_submesh_01(void **state);
+static bool test_steps_submesh_01(void);
+
+static char event_node_name[][10] = {"CORENODE1", "APP1NODE1", "APP2NODE1", "CORENODE2",
+                                     "APP1NODE2", "APP2NODE2"
+                                    };
+static const char *node_ids[] = { "corenode1", "app1node1", "app2node1", "corenode2",
+                                  "app1node2", "app2node2"
+                                };
+
+static mesh_event_t core_node1[] = { NODE_STARTED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t core_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app1_node1[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app2_node1[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app1_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, MESH_EVENT_COMPLETED};
+
+static mesh_event_t app2_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, MESH_EVENT_COMPLETED};
+
+/* State structure for SubMesh Test Case #1 */
+static char *test_case_submesh_1_nodes[] = { "corenode1", "app1node1", "app2node1", "corenode2", "app1node2", "app2node2" };
+static black_box_state_t test_case_submesh_1_state = {
+       .test_case_name =  "test_cases_submesh01",
+       .node_names =  test_case_submesh_1_nodes,
+       .num_nodes =  6
+};
+
+static int black_box_group0_setup(void **state) {
+       const char *nodes[] = { "corenode1", "app1node1", "app2node1", "corenode2", "app1node2", "app2node2" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+
+       return 0;
+}
+
+static int black_box_group0_teardown(void **state) {
+       PRINT_TEST_CASE_MSG("Destroying Containers\n");
+       destroy_containers();
+
+       return 0;
+}
+
+static int black_box_all_nodes_setup(void **state) {
+       const char *nodes[] = { "corenode1" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+       PRINT_TEST_CASE_MSG("Created Containers\n");
+       return 0;
+}
+
+static bool event_cb(mesh_event_payload_t payload) {
+       static node_status_t node_status[6] = {
+               {core_node1, 0, 3, false},
+               {app1_node1, 0, 4, false},
+               {app2_node1, 0, 4, false},
+               {core_node2, 0, 4, false},
+               {app1_node2, 0, 7, false},
+               {app2_node2, 0, 7, false}
+       };
+
+       fprintf(stderr, "%s(%lu) : %s\n", event_node_name[payload.client_id], time(NULL), event_status[payload.mesh_event]);
+       assert(change_state(&node_status[payload.client_id], payload.mesh_event));
+
+       if(payload.mesh_event == NODE_JOINED) {
+               signal_node_start(node_status, 1, 5, node_ids);
+       }
+
+       if(check_nodes_finished(node_status, 6)) {
+               test_case_status = true;
+               return true;
+       }
+
+       return false;
+}
+
+/* Execute SubMesh Test Case # 1 */
+static void test_case_submesh_01(void **state) {
+       execute_test(test_steps_submesh_01, state);
+}
+
+/* Test Steps for SubMesh Test Case # 1
+
+    Test Steps:
+    1. Run corenode1, app1node1, app2node1, corenode2, app1node2 and app2node2
+    2. Generate invites to app1node1, app2node1, corenode2, app1node2 and app2node2
+        from corenode1 to join corenode1.
+    3. After Join is successful start channels from all nodes and exchange data on channels
+    4. Try to fetch the node handle of one sub-mesh node from node in another sub-mesh
+
+    Expected Result:
+    Channels should be formed between nodes of sub-mesh & coremesh, nodes with in sub-mesh
+    and should be able to exchange data. But node in one sub-mesh should not get the details
+    of node in another sub-mesh.
+*/
+static bool test_steps_submesh_01(void) {
+       char *invite_corenode2, *invite_app1node1, *invite_app2node1, *invite_app1node2, *invite_app2node2;
+       bool result = false;
+       int i;
+       char *import;
+       pthread_t thread1, thread2;
+
+       import = mesh_event_sock_create(eth_if_name);
+       invite_corenode2 = invite_in_container("corenode1", "corenode2");
+       invite_app1node1 = submesh_invite_in_container("corenode1", "app1node1", "app1");
+       invite_app2node1 = submesh_invite_in_container("corenode1", "app2node1", "app2");
+       invite_app1node2 = submesh_invite_in_container("corenode1", "app1node2", "app1");
+       invite_app2node2 = submesh_invite_in_container("corenode1", "app2node2", "app2");
+
+       node_sim_in_container_event("corenode1", "1", NULL, CORENODE1_ID, import);
+       node_sim_in_container_event("corenode2", "1", invite_corenode2, CORENODE2_ID, import);
+       node_sim_in_container_event("app1node1", "1", invite_app1node1, APP1NODE1_ID, import);
+       node_sim_in_container_event("app2node1", "1", invite_app2node1, APP2NODE1_ID, import);
+       node_sim_in_container_event("app1node2", "1", invite_app1node2, APP1NODE2_ID, import);
+       node_sim_in_container_event("app2node2", "1", invite_app2node2, APP2NODE2_ID, import);
+
+       PRINT_TEST_CASE_MSG("Waiting for nodes to get connected with corenode1\n");
+
+       assert(wait_for_event(event_cb, 240));
+       assert(test_case_status);
+
+       free(invite_corenode2);
+       free(invite_app1node1);
+       free(invite_app2node1);
+       free(invite_app1node2);
+       free(invite_app2node2);
+
+       mesh_event_destroy();
+
+       return true;
+}
+
+int test_cases_submesh01(void) {
+       const struct CMUnitTest blackbox_group0_tests[] = {
+               cmocka_unit_test_prestate_setup_teardown(test_case_submesh_01, setup_test, teardown_test,
+                               (void *)&test_case_submesh_1_state)
+       };
+       total_tests += sizeof(blackbox_group0_tests) / sizeof(blackbox_group0_tests[0]);
+
+       return cmocka_run_group_tests(blackbox_group0_tests, black_box_group0_setup, black_box_group0_teardown);
+}
\ No newline at end of file
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh01.h b/test/blackbox/run_blackbox_tests/test_cases_submesh01.h
new file mode 100644 (file)
index 0000000..d4ebcca
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef TEST_CASES_SUBMESH_H
+#define TEST_CASES_SUBMESH_H
+
+/*
+    test_cases_submesh01.h -- Declarations for Individual Test Case implementation functions
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+
+#include <stdbool.h>
+#include "../common/mesh_event_handler.h"
+
+extern int total_tests;
+extern int test_cases_submesh01(void);
+
+#endif // TEST_CASES_SUBMESH_H
\ No newline at end of file
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh02.c b/test/blackbox/run_blackbox_tests/test_cases_submesh02.c
new file mode 100644 (file)
index 0000000..018adbf
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+    test_cases_submesh02.c -- Execution of specific meshlink black box test cases
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <assert.h>
+#include "execute_tests.h"
+#include "test_cases_submesh02.h"
+#include "pthread.h"
+#include "../common/containers.h"
+#include "../common/test_step.h"
+#include "../common/common_handlers.h"
+#include "../common/mesh_event_handler.h"
+
+#define CORENODE1_ID  "0"
+#define APP1NODE1_ID  "1"
+#define APP2NODE1_ID  "2"
+#define CORENODE2_ID  "3"
+#define APP1NODE2_ID  "4"
+#define APP2NODE2_ID  "5"
+
+#define INIT_ST         0
+
+static bool test_case_status = false;
+
+static void test_case_submesh_02(void **state);
+static bool test_steps_submesh_02(void);
+
+static char event_node_name[][10] = {"CORENODE1", "APP1NODE1", "APP2NODE1", "CORENODE2",
+                                     "APP1NODE2", "APP2NODE2"
+                                    };
+static const char *node_ids[] = { "corenode1", "app1node1", "app2node1", "corenode2",
+                                  "app1node2", "app2node2"
+                                };
+
+static mesh_event_t core_node1[] = { NODE_STARTED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t core_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app1_node1[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app2_node1[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app1_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, MESH_EVENT_COMPLETED};
+
+static mesh_event_t app2_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, MESH_EVENT_COMPLETED};
+
+/* State structure for SubMesh Test Case #1 */
+static char *test_case_submesh_2_nodes[] = { "corenode1", "app1node1", "app2node1", "corenode2", "app1node2", "app2node2" };
+static black_box_state_t test_case_submesh_2_state = {
+       .test_case_name =  "test_cases_submesh02",
+       .node_names =  test_case_submesh_2_nodes,
+       .num_nodes =  6
+};
+
+static int black_box_group0_setup(void **state) {
+       const char *nodes[] = { "corenode1", "app1node1", "app2node1", "corenode2", "app1node2", "app2node2" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+
+       return 0;
+}
+
+static int black_box_group0_teardown(void **state) {
+       PRINT_TEST_CASE_MSG("Destroying Containers\n");
+       destroy_containers();
+
+       return 0;
+}
+
+static int black_box_all_nodes_setup(void **state) {
+       const char *nodes[] = { "corenode1" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+       PRINT_TEST_CASE_MSG("Created Containers\n");
+       return 0;
+}
+
+static bool event_cb(mesh_event_payload_t payload) {
+       static node_status_t node_status[6] = {
+               {core_node1, 0, 3, false},
+               {app1_node1, 0, 4, false},
+               {app2_node1, 0, 4, false},
+               {core_node2, 0, 4, false},
+               {app1_node2, 0, 7, false},
+               {app2_node2, 0, 7, false}
+       };
+
+       fprintf(stderr, "%s(%lu) : %s\n", event_node_name[payload.client_id], time(NULL), event_status[payload.mesh_event]);
+       assert(change_state(&node_status[payload.client_id], payload.mesh_event));
+
+       if(payload.mesh_event == NODE_JOINED) {
+               signal_node_start(node_status, 1, 5, node_ids);
+       }
+
+       if(check_nodes_finished(node_status, 6)) {
+               test_case_status = true;
+               return true;
+       }
+
+       return false;
+}
+
+/* Execute SubMesh Test Case # 2 */
+static void test_case_submesh_02(void **state) {
+       execute_test(test_steps_submesh_02, state);
+}
+
+/* Test Steps for SubMesh Test Case # 2
+
+    Test Steps:
+    1. Run corenode1, app1node1, app2node1, corenode2, app1node2 and app2node2
+    2. Generate invites to app1node1, app2node1, corenode2, app1node2 and app2node2
+        from corenode1 to join corenode1.
+    3. After Join is successful start channels from all nodes and exchange data on channels
+    4. Try to fetch the list of all nodes and check if the nodes in other submesh doesnot
+       appear in the list.
+    5. Try fetch all the nodes with a submesh handle and check only if both the nodes joining
+       the submesh are present.
+
+    Expected Result:
+    Channels should be formed between nodes of sub-mesh & coremesh, nodes with in sub-mesh
+    and should be able to exchange data. Lis of all nodes should only conatin four nodes
+    and the list of submesh should only conatin two nodes of that submesh.
+*/
+static bool test_steps_submesh_02(void) {
+       char *invite_corenode2, *invite_app1node1, *invite_app2node1, *invite_app1node2, *invite_app2node2;
+       bool result = false;
+       int i;
+       char *import;
+       pthread_t thread1, thread2;
+
+       import = mesh_event_sock_create(eth_if_name);
+       invite_corenode2 = invite_in_container("corenode1", "corenode2");
+       invite_app1node1 = submesh_invite_in_container("corenode1", "app1node1", "app1");
+       invite_app2node1 = submesh_invite_in_container("corenode1", "app2node1", "app2");
+       invite_app1node2 = submesh_invite_in_container("corenode1", "app1node2", "app1");
+       invite_app2node2 = submesh_invite_in_container("corenode1", "app2node2", "app2");
+
+       node_sim_in_container_event("corenode1", "1", NULL, CORENODE1_ID, import);
+       node_sim_in_container_event("corenode2", "1", invite_corenode2, CORENODE2_ID, import);
+       node_sim_in_container_event("app1node1", "1", invite_app1node1, APP1NODE1_ID, import);
+       node_sim_in_container_event("app2node1", "1", invite_app2node1, APP2NODE1_ID, import);
+       node_sim_in_container_event("app1node2", "1", invite_app1node2, APP1NODE2_ID, import);
+       node_sim_in_container_event("app2node2", "1", invite_app2node2, APP2NODE2_ID, import);
+
+       PRINT_TEST_CASE_MSG("Waiting for nodes to get connected with corenode1\n");
+
+       assert(wait_for_event(event_cb, 240));
+       assert(test_case_status);
+
+       free(invite_corenode2);
+       free(invite_app1node1);
+       free(invite_app2node1);
+       free(invite_app1node2);
+       free(invite_app2node2);
+
+       mesh_event_destroy();
+
+       return true;
+}
+
+int test_cases_submesh02(void) {
+       const struct CMUnitTest blackbox_group0_tests[] = {
+               cmocka_unit_test_prestate_setup_teardown(test_case_submesh_02, setup_test, teardown_test,
+                               (void *)&test_case_submesh_2_state)
+       };
+       total_tests += sizeof(blackbox_group0_tests) / sizeof(blackbox_group0_tests[0]);
+
+       return cmocka_run_group_tests(blackbox_group0_tests, black_box_group0_setup, black_box_group0_teardown);
+}
\ No newline at end of file
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh02.h b/test/blackbox/run_blackbox_tests/test_cases_submesh02.h
new file mode 100644 (file)
index 0000000..a571038
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef TEST_CASES_SUBMESH02_H
+#define TEST_CASES_SUBMESH02_H
+
+/*
+    test_cases_submesh02.h -- Declarations for Individual Test Case implementation functions
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+
+#include <stdbool.h>
+#include "../common/mesh_event_handler.h"
+
+extern int total_tests;
+extern int test_cases_submesh02(void);
+
+#endif // TEST_CASES_SUBMESH02_H
\ No newline at end of file
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh03.c b/test/blackbox/run_blackbox_tests/test_cases_submesh03.c
new file mode 100644 (file)
index 0000000..76b6ef9
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+    test_cases_submesh03.c -- Execution of specific meshlink black box test cases
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <assert.h>
+#include "execute_tests.h"
+#include "test_cases_submesh03.h"
+#include "pthread.h"
+#include "../common/containers.h"
+#include "../common/test_step.h"
+#include "../common/common_handlers.h"
+#include "../common/mesh_event_handler.h"
+
+#define CORENODE1_ID  "0"
+#define APP1NODE1_ID  "1"
+#define APP1NODE2_ID  "2"
+
+#define INIT_ST         0
+
+static bool test_case_status = false;
+
+static void test_case_submesh_03(void **state);
+static bool test_steps_submesh_03(void);
+
+static char event_node_name[][10] = {"CORENODE1", "APP1NODE1", "APP1NODE2"};
+static const char *node_ids[] = { "corenode1", "app1node1", "app1node2" };
+
+static mesh_event_t core_node1[] = { NODE_STARTED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app1_node1[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED};
+
+static mesh_event_t app1_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, MESH_EVENT_COMPLETED};
+
+static node_status_t node_status[3] = {
+       {core_node1, 0, 3, false},
+       {app1_node1, 0, 4, false},
+       {app1_node2, 0, 7, false}
+};
+
+/* State structure for SubMesh Test Case #3 */
+static char *test_case_submesh_3_nodes[] = { "corenode1", "app1node1", "app1node2" };
+static black_box_state_t test_case_submesh_3_state = {
+       .test_case_name =  "test_cases_submesh03",
+       .node_names =  test_case_submesh_3_nodes,
+       .num_nodes =  3
+};
+
+static int black_box_group0_setup(void **state) {
+       const char *nodes[] = { "corenode1", "app1node1", "app1node2" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+
+       return 0;
+}
+
+static int black_box_group0_teardown(void **state) {
+       PRINT_TEST_CASE_MSG("Destroying Containers\n");
+       destroy_containers();
+
+       return 0;
+}
+
+static int black_box_all_nodes_setup(void **state) {
+       const char *nodes[] = { "corenode1" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+       PRINT_TEST_CASE_MSG("Created Containers\n");
+       return 0;
+}
+
+static void restart_all_nodes(char *import) {
+       int i;
+
+       for(i = 0; i < 3; i++) {
+               node_step_in_container(node_ids[i], "SIGTERM");
+               node_status[i].current_index = 0;
+       }
+
+       sleep(5);
+
+       node_sim_in_container_event("corenode1", "1", NULL, CORENODE1_ID, import);
+       node_sim_in_container_event("app1node1", "1", NULL, APP1NODE1_ID, import);
+       node_sim_in_container_event("app1node2", "1", NULL, APP1NODE2_ID, import);
+}
+
+static bool event_cb(mesh_event_payload_t payload) {
+
+       fprintf(stderr, "%s(%lu) : %s\n", event_node_name[payload.client_id], time(NULL), event_status[payload.mesh_event]);
+       assert(change_state(&node_status[payload.client_id], payload.mesh_event));
+
+       if(payload.mesh_event == NODE_JOINED) {
+               signal_node_start(node_status, 1, 2, node_ids);
+       }
+
+       if(check_nodes_finished(node_status, 3)) {
+               test_case_status = true;
+               return true;
+       }
+
+       return false;
+}
+
+/* Execute SubMesh Test Case # 3 */
+static void test_case_submesh_03(void **state) {
+       execute_test(test_steps_submesh_03, state);
+}
+
+/* Test Steps for SubMesh Test Case # 3
+
+    Test Steps:
+    1. Run corenode1, app1node1, and app1node2
+    2. Generate invites to app1node1, and app1node2
+        from corenode1 to join corenode1.
+    3. After Join is successful start channels from all nodes and exchange data on channels
+    4. Try to restart all the nodes at the same time.
+
+    Expected Result:
+    Channels should be formed between nodes of sub-mesh & coremesh, nodes with in sub-mesh
+    and should be able to exchange data even after restart.
+*/
+static bool test_steps_submesh_03(void) {
+       char *invite_app1node1, *invite_app1node2;
+       bool result = false;
+       int i;
+       char *import;
+       pthread_t thread1, thread2;
+
+       import = mesh_event_sock_create(eth_if_name);
+       invite_app1node1 = invite_in_container("corenode1", "app1node1");
+       invite_app1node2 = invite_in_container("corenode1", "app1node2");
+
+       node_sim_in_container_event("corenode1", "1", NULL, CORENODE1_ID, import);
+       node_sim_in_container_event("app1node1", "1", invite_app1node1, APP1NODE1_ID, import);
+       node_sim_in_container_event("app1node2", "1", invite_app1node2, APP1NODE2_ID, import);
+
+       PRINT_TEST_CASE_MSG("Waiting for nodes to get connected with corenode1\n");
+
+       assert(wait_for_event(event_cb, 120));
+       assert(test_case_status);
+
+       test_case_status = false;
+
+       restart_all_nodes(import);
+       PRINT_TEST_CASE_MSG("Waiting for nodes to get restarted\n");
+
+       assert(wait_for_event(event_cb, 120));
+       assert(test_case_status);
+
+       free(invite_app1node1);
+       free(invite_app1node2);
+
+       mesh_event_destroy();
+
+       return true;
+}
+
+int test_cases_submesh03(void) {
+       const struct CMUnitTest blackbox_group0_tests[] = {
+               cmocka_unit_test_prestate_setup_teardown(test_case_submesh_03, setup_test, teardown_test,
+                               (void *)&test_case_submesh_3_state)
+       };
+       total_tests += sizeof(blackbox_group0_tests) / sizeof(blackbox_group0_tests[0]);
+
+       return cmocka_run_group_tests(blackbox_group0_tests, black_box_group0_setup, black_box_group0_teardown);
+}
\ No newline at end of file
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh03.h b/test/blackbox/run_blackbox_tests/test_cases_submesh03.h
new file mode 100644 (file)
index 0000000..432c1e0
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef TEST_CASES_SUBMESH03_H
+#define TEST_CASES_SUBMESH03_H
+
+/*
+    test_cases_submesh03.h -- Declarations for Individual Test Case implementation functions
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+
+#include <stdbool.h>
+#include "../common/mesh_event_handler.h"
+
+extern int total_tests;
+extern int test_cases_submesh03(void);
+
+#endif // TEST_CASES_SUBMESH03_H
\ No newline at end of file
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh04.c b/test/blackbox/run_blackbox_tests/test_cases_submesh04.c
new file mode 100644 (file)
index 0000000..96c117a
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+    test_cases_submesh05.c -- Execution of specific meshlink black box test cases
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <assert.h>
+#include "execute_tests.h"
+#include "test_cases_submesh04.h"
+#include "pthread.h"
+#include "../common/containers.h"
+#include "../common/test_step.h"
+#include "../common/common_handlers.h"
+#include "../common/mesh_event_handler.h"
+
+#define CORENODE1_ID  "0"
+#define APP1NODE1_ID  "1"
+#define APP1NODE2_ID  "2"
+
+#define INIT_ST         0
+
+static bool test_case_status = false;
+
+static void test_case_submesh_04(void **state);
+static bool test_steps_submesh_04(void);
+
+static char event_node_name[][10] = {"CORENODE1", "APP1NODE1", "APP1NODE2"};
+static const char *node_ids[] = { "corenode1", "app1node1", "app1node2" };
+
+static mesh_event_t core_node1[] = { NODE_STARTED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED };
+
+static mesh_event_t app1_node1[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED };
+
+static mesh_event_t app1_node2[] = { NODE_STARTED, NODE_JOINED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, CHANNEL_OPENED, CHANNEL_DATA_RECIEVED, MESH_EVENT_COMPLETED};
+
+/* State structure for SubMesh Test Case #4 */
+static char *test_case_submesh_4_nodes[] = { "corenode1", "app1node1", "app1node2" };
+static black_box_state_t test_case_submesh_4_state = {
+       .test_case_name =  "test_cases_submesh04",
+       .node_names =  test_case_submesh_4_nodes,
+       .num_nodes =  3
+};
+
+static int black_box_group0_setup(void **state) {
+       const char *nodes[] = { "corenode1", "app1node1", "app1node2" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+
+       return 0;
+}
+
+static int black_box_group0_teardown(void **state) {
+       PRINT_TEST_CASE_MSG("Destroying Containers\n");
+       destroy_containers();
+
+       return 0;
+}
+
+static int black_box_all_nodes_setup(void **state) {
+       const char *nodes[] = { "corenode1" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       PRINT_TEST_CASE_MSG("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+       PRINT_TEST_CASE_MSG("Created Containers\n");
+       return 0;
+}
+
+static bool event_cb(mesh_event_payload_t payload) {
+       static node_status_t node_status[3] = {
+               {core_node1, 0, 3, false},
+               {app1_node1, 0, 4, false},
+               {app1_node2, 0, 7, false}
+       };
+
+       fprintf(stderr, "%s(%lu) : %s\n", event_node_name[payload.client_id], time(NULL), event_status[payload.mesh_event]);
+       assert(change_state(&node_status[payload.client_id], payload.mesh_event));
+
+       if(payload.mesh_event == NODE_JOINED) {
+               signal_node_start(node_status, 1, 2, node_ids);
+       }
+
+       if(check_nodes_finished(node_status, 3)) {
+               test_case_status = true;
+               return true;
+       }
+
+       return false;
+}
+
+/* Execute SubMesh Test Case # 4 */
+static void test_case_submesh_04(void **state) {
+       execute_test(test_steps_submesh_04, state);
+}
+
+/* Test Steps for SubMesh Test Case # 4
+
+    Test Steps:
+    1. Run corenode1, app1node1, and app1node2
+    2. Generate invites to app1node1, app1node2
+        from corenode1 to join corenode1.
+    3. After Join is successful start channels from all nodes and exchange data on channels
+    4. Black list a node in the submesh and check if it is successful
+    5. White list the node and it should be form all the connections again
+
+    Expected Result:
+    Channels should be formed between nodes of sub-mesh & coremesh, nodes with in sub-mesh
+    and should be able to exchange data. When black listed, other node should not get any
+    from the black listed node. When white listed again it has to form the connections as
+    they were previously before black listing.
+*/
+static bool test_steps_submesh_04(void) {
+       char *invite_app1node1, *invite_app1node2;
+       bool result = false;
+       int i;
+       char *import;
+       pthread_t thread1, thread2;
+
+       import = mesh_event_sock_create(eth_if_name);
+       invite_app1node1 = invite_in_container("corenode1", "app1node1");
+       invite_app1node2 = invite_in_container("corenode1", "app1node2");
+
+       node_sim_in_container_event("corenode1", "1", NULL, CORENODE1_ID, import);
+       node_sim_in_container_event("app1node1", "1", invite_app1node1, APP1NODE1_ID, import);
+       node_sim_in_container_event("app1node2", "1", invite_app1node2, APP1NODE2_ID, import);
+
+       PRINT_TEST_CASE_MSG("Waiting for nodes to get connected with corenode1\n");
+
+       assert(wait_for_event(event_cb, 120));
+       assert(test_case_status);
+
+       free(invite_app1node1);
+       free(invite_app1node2);
+
+       mesh_event_destroy();
+
+       return true;
+}
+
+int test_cases_submesh04(void) {
+       const struct CMUnitTest blackbox_group0_tests[] = {
+               cmocka_unit_test_prestate_setup_teardown(test_case_submesh_04, setup_test, teardown_test,
+                               (void *)&test_case_submesh_4_state)
+       };
+       total_tests += sizeof(blackbox_group0_tests) / sizeof(blackbox_group0_tests[0]);
+
+       return cmocka_run_group_tests(blackbox_group0_tests, black_box_group0_setup, black_box_group0_teardown);
+}
\ No newline at end of file
diff --git a/test/blackbox/run_blackbox_tests/test_cases_submesh04.h b/test/blackbox/run_blackbox_tests/test_cases_submesh04.h
new file mode 100644 (file)
index 0000000..be7a22b
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef TEST_CASES_SUBMESH04_H
+#define TEST_CASES_SUBMESH04_H
+
+/*
+    test_cases_submesh04.h -- Declarations for Individual Test Case implementation functions
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+
+#include <stdbool.h>
+#include "../common/mesh_event_handler.h"
+
+extern int total_tests;
+extern int test_cases_submesh04(void);
+
+#endif // TEST_CASES_SUBMESH04_H
\ No newline at end of file
index 4cca831a7d35902544c615c8b46897aa1cdb8e54..177c575f07ae4dd4ec040b7b830f3d21fd6e03e2 100644 (file)
@@ -6,4 +6,4 @@ node_sim_peer_CFLAGS = -D_GNU_SOURCE
 
 node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
 node_sim_nut_LDADD = ../../../src/libmeshlink.la
-node_sim_nut_CFLAGS = -D_GNU_SOURCE
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
\ No newline at end of file
index 7f4ddd9010f660cbcbb86cb35ac9694d4a348051..358eadcddeaabf51727e23c57cb03dd1e959b232 100644 (file)
@@ -100,7 +100,7 @@ int main(int argc, char *argv[]) {
 
        if(!argv[CMD_LINE_ARG_INVITEURL]) {
                fprintf(stderr, "Generating Inviation to PEER\n");
-               invite_peer = execute_invite("peer");
+               invite_peer = execute_invite("peer", NULL);
                assert(invite_peer != NULL);
 
                if(!mesh_event_sock_send(clientId, NODE_INVITATION, invite_peer, strlen(invite_peer) + 1)) {
index 8741ffa75d884c0e97419e7c7a0cfb49ff0d36d6..e0459e094788160fd24227578ebefca665124fe6 100644 (file)
@@ -103,7 +103,7 @@ int main(int argc, char *argv[]) {
        }
 
        fprintf(stderr, "Generating Inviation to PEER\n");
-       invite_peer = execute_invite("peer");
+       invite_peer = execute_invite("peer", NULL);
        assert(invite_peer != NULL);
 
        if(!mesh_event_sock_send(clientId, NODE_INVITATION, invite_peer, strlen(invite_peer) + 1)) {
diff --git a/test/blackbox/test_cases_submesh01/Makefile.am b/test/blackbox/test_cases_submesh01/Makefile.am
new file mode 100644 (file)
index 0000000..bb0ff44
--- /dev/null
@@ -0,0 +1,25 @@
+check_PROGRAMS = node_sim_corenode1 node_sim_corenode2 node_sim_app1node1 node_sim_app1node2 node_sim_app2node1 node_sim_app2node2
+
+node_sim_corenode1_SOURCES = node_sim_corenode1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_corenode1_LDADD = ../../../src/libmeshlink.la
+node_sim_corenode1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_corenode2_SOURCES = node_sim_corenode2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_corenode2_LDADD = ../../../src/libmeshlink.la
+node_sim_corenode2_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node1_SOURCES = node_sim_app1node1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node1_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node2_SOURCES = node_sim_app1node2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node2_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node2_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app2node1_SOURCES = node_sim_app2node1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app2node1_LDADD = ../../../src/libmeshlink.la
+node_sim_app2node1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app2node2_SOURCES = node_sim_app2node2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app2node2_LDADD = ../../../src/libmeshlink.la
+node_sim_app2node2_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_cases_submesh01/node_sim_app1node1.c b/test/blackbox/test_cases_submesh01/node_sim_app1node1.c
new file mode 100644 (file)
index 0000000..50cf4fe
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node1 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       fprintf(stderr, "\tapp1node1 rejecting channel request from %s at %lu\n", channel->node->name, time(NULL));
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+       char *message = "Channel Message";
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node1 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("corenode1", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node1\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app1node1' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node1 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 100));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 100));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh01/node_sim_app1node2.c b/test/blackbox/test_cases_submesh01/node_sim_app1node2.c
new file mode 100644 (file)
index 0000000..bb8697b
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag app_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node2 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node2 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node2 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else {
+               assert(false);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node2's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       } else if(!strcasecmp(node->name, "app1node1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode app1node1 became reachable\n");
+                       set_sync_flag(&app_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app1node2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node2conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // Open a channel to peer node
+       channel_opened.flag = false;
+       channel_data_recieved.flag = false;
+
+       assert(wait_sync_flag(&app_reachable, 60));
+
+       core_node = meshlink_get_node(mesh, "app1node1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to app1node1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 100));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 100));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       core_node = meshlink_get_node(mesh, "app2node1");
+
+       if(NULL != core_node) {
+               send_event(SIG_ABORT);
+               assert(false);
+       }
+
+       send_event(MESH_EVENT_COMPLETED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh01/node_sim_app2node1.c b/test/blackbox/test_cases_submesh01/node_sim_app2node1.c
new file mode 100644 (file)
index 0000000..ced45ae
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp2node1 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       } else if(!strcmp(channel->node->name, "app2node2")) {
+               fprintf(stderr, "\tapp2node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       fprintf(stderr, "\tapp2node1 rejecting channel request from %s at %lu\n", channel->node->name, time(NULL));
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char *message = "Channel Message";
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp2node1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app2node2")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp2node1's Channel request has been accepted by corenode1 at : %lu\n", time(NULL));
+
+       if(0 == strcmp("corenode1", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app2node1\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app2node1' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app2node1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp2node1 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh01/node_sim_app2node2.c b/test/blackbox/test_cases_submesh01/node_sim_app2node2.c
new file mode 100644 (file)
index 0000000..4323988
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag app_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp2node2 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp2node2 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp2node2 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app2node1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else {
+               assert(false);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp2node2's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       } else if(!strcasecmp(node->name, "app2node1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode app2node1 became reachable\n");
+                       set_sync_flag(&app_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app2node2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app2node2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app2node2conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp2node2 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 30));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // Open a channel to peer node
+       channel_opened.flag = false;
+       channel_data_recieved.flag = false;
+
+       assert(wait_sync_flag(&app_reachable, 60));
+
+       core_node = meshlink_get_node(mesh, "app2node1");
+       assert(core_node);
+       fprintf(stderr, "\tapp2node2 Sending Channel request to app2node1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 30));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       core_node = meshlink_get_node(mesh, "app1node1");
+
+       if(NULL != core_node) {
+               send_event(SIG_ABORT);
+               assert(false);
+       }
+
+       send_event(MESH_EVENT_COMPLETED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh01/node_sim_corenode1.c b/test/blackbox/test_cases_submesh01/node_sim_corenode1.c
new file mode 100644 (file)
index 0000000..842e5d7
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+    node_sim.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static bool conn_status = false;
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static meshlink_handle_t *mesh = NULL;
+
+static void mesh_send_message_handler(char *destination);
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "corenode1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!memcmp(dat, "Channel Message", len)) {
+               mesh_send_message_handler(channel->node->name);
+
+               if(0 == strcmp("corenode2", channel->node->name)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               }
+       } else if(!memcmp(dat, "failure", 7)) {
+               assert(false);
+       }
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(reachable) {
+               fprintf(stderr, "Node %s became reachable\n", node->name);
+       } else {
+               fprintf(stderr, "Node %s is unreachable\n", node->name);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "corenode1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("corenode2", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+/* channel receive callback */
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "corenode1 got channel request from %s\n", channel->node->name);
+       meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+
+       return true;
+}
+
+void mesh_send_message_handler(char *destination) {
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *target_node = NULL;
+
+       // Open a channel to destination node
+       target_node = meshlink_get_node(mesh, destination);
+       assert(target_node);
+       fprintf(stderr, "corenode1 Sending Channel request to %s at : %lu\n", destination, time(NULL));
+       channel = meshlink_channel_open(mesh, target_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+       int i;
+
+       // Import mesh event handler
+
+       fprintf(stderr, "Mesh node 'corenode1' starting up........\n");
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       setup_signals();
+
+       // Execute test steps
+
+       mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 10));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}
diff --git a/test/blackbox/test_cases_submesh01/node_sim_corenode2.c b/test/blackbox/test_cases_submesh01/node_sim_corenode2.c
new file mode 100644 (file)
index 0000000..a7b4760
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "corenode2 got channel request from %s", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "corenode2 got message from %s as %s", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "corenode2's Channel request has been accepted by corenode1 at : %lu", time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "Node corenode2 became reachable");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in corenode2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "Mesh node 'corenode2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("corenode1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "corenode2 Sending Channel request to corenode1 at : %lu", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 10));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh02/Makefile.am b/test/blackbox/test_cases_submesh02/Makefile.am
new file mode 100644 (file)
index 0000000..bb0ff44
--- /dev/null
@@ -0,0 +1,25 @@
+check_PROGRAMS = node_sim_corenode1 node_sim_corenode2 node_sim_app1node1 node_sim_app1node2 node_sim_app2node1 node_sim_app2node2
+
+node_sim_corenode1_SOURCES = node_sim_corenode1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_corenode1_LDADD = ../../../src/libmeshlink.la
+node_sim_corenode1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_corenode2_SOURCES = node_sim_corenode2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_corenode2_LDADD = ../../../src/libmeshlink.la
+node_sim_corenode2_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node1_SOURCES = node_sim_app1node1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node1_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node2_SOURCES = node_sim_app1node2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node2_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node2_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app2node1_SOURCES = node_sim_app2node1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app2node1_LDADD = ../../../src/libmeshlink.la
+node_sim_app2node1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app2node2_SOURCES = node_sim_app2node2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app2node2_LDADD = ../../../src/libmeshlink.la
+node_sim_app2node2_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_cases_submesh02/node_sim_app1node1.c b/test/blackbox/test_cases_submesh02/node_sim_app1node1.c
new file mode 100644 (file)
index 0000000..d542357
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node1 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       fprintf(stderr, "\tapp1node1 rejecting channel request from %s at %lu\n", channel->node->name, time(NULL));
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+       char *message = "Channel Message";
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node1 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("corenode1", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node1\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app1node1' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node1 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh02/node_sim_app1node2.c b/test/blackbox/test_cases_submesh02/node_sim_app1node2.c
new file mode 100644 (file)
index 0000000..3656b29
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag app_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node2 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node2 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node2 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else {
+               assert(false);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node2's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       } else if(!strcasecmp(node->name, "app1node1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode app1node1 became reachable\n");
+                       set_sync_flag(&app_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       size_t num_nodes, i;
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+       meshlink_node_t **node_handles = NULL;
+
+       fprintf(stderr, "\tMesh node 'app1node2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node2conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // Open a channel to peer node
+       channel_opened.flag = false;
+       channel_data_recieved.flag = false;
+
+       assert(wait_sync_flag(&app_reachable, 60));
+
+       core_node = meshlink_get_node(mesh, "app1node1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to app1node1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 30));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       num_nodes = 0;
+       node_handles = meshlink_get_all_nodes(mesh, NULL, &num_nodes);
+       fprintf(stderr, "\tGot %d nodes in list with error : %s\n", num_nodes, meshlink_strerror(meshlink_errno));
+       assert(node_handles);
+       assert((num_nodes == 4));
+
+       for(i = 0; i < num_nodes; i++) {
+               fprintf(stderr, "\tChecking the node : %s\n", node_handles[i]->name);
+
+               if((0 == strcmp(node_handles[i]->name, "app2node1")) || (0 == strcmp(node_handles[i]->name, "app2node2"))) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               }
+       }
+
+       meshlink_node_t *node = meshlink_get_self(mesh);
+       assert(node);
+       meshlink_submesh_t *submesh = meshlink_get_node_submesh(mesh, node);
+       assert(submesh);
+
+       node_handles = meshlink_get_all_nodes_by_submesh(mesh, submesh, node_handles, &num_nodes);
+       assert(node_handles);
+       assert((num_nodes == 2));
+
+       for(i = 0; i < num_nodes; i++) {
+               fprintf(stderr, "\tChecking the node : %s\n", node_handles[i]->name);
+
+               if((0 == strcmp(node_handles[i]->name, "app2node1")) || (0 == strcmp(node_handles[i]->name, "app2node2"))) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               }
+       }
+
+       send_event(MESH_EVENT_COMPLETED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh02/node_sim_app2node1.c b/test/blackbox/test_cases_submesh02/node_sim_app2node1.c
new file mode 100644 (file)
index 0000000..ced45ae
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp2node1 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       } else if(!strcmp(channel->node->name, "app2node2")) {
+               fprintf(stderr, "\tapp2node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       fprintf(stderr, "\tapp2node1 rejecting channel request from %s at %lu\n", channel->node->name, time(NULL));
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char *message = "Channel Message";
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp2node1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app2node2")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp2node1's Channel request has been accepted by corenode1 at : %lu\n", time(NULL));
+
+       if(0 == strcmp("corenode1", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app2node1\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app2node1' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app2node1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp2node1 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh02/node_sim_app2node2.c b/test/blackbox/test_cases_submesh02/node_sim_app2node2.c
new file mode 100644 (file)
index 0000000..b87a484
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag app_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp2node2 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp2node2 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp2node2 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app2node1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else {
+               assert(false);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp2node2's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       } else if(!strcasecmp(node->name, "app2node1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode app2node1 became reachable\n");
+                       set_sync_flag(&app_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app2node2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       size_t num_nodes, i;
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+       meshlink_node_t **node_handles = NULL;
+
+       fprintf(stderr, "\tMesh node 'app2node2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app2node2conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp2node2 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 30));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // Open a channel to peer node
+       channel_opened.flag = false;
+       channel_data_recieved.flag = false;
+
+       assert(wait_sync_flag(&app_reachable, 60));
+
+       core_node = meshlink_get_node(mesh, "app2node1");
+       assert(core_node);
+       fprintf(stderr, "\tapp2node2 Sending Channel request to app2node1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       num_nodes = 0;
+       node_handles = meshlink_get_all_nodes(mesh, NULL, &num_nodes);
+       fprintf(stderr, "\tGot %d nodes in list with error : %s\n", num_nodes, meshlink_strerror(meshlink_errno));
+       assert(node_handles);
+       assert((num_nodes == 4));
+
+       for(i = 0; i < num_nodes; i++) {
+               fprintf(stderr, "\tChecking the node : %s\n", node_handles[i]->name);
+
+               if(0 == strcmp(node_handles[i]->name, "app1node1")) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               } else if(0 == strcmp(node_handles[i]->name, "app1node2")) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               }
+       }
+
+       meshlink_node_t *node = meshlink_get_self(mesh);
+       assert(node);
+       meshlink_submesh_t *submesh = meshlink_get_node_submesh(mesh, node);
+       assert(submesh);
+
+       node_handles = meshlink_get_all_nodes_by_submesh(mesh, submesh, node_handles, &num_nodes);
+       assert(node_handles);
+       assert((num_nodes == 2));
+
+       for(i = 0; i < num_nodes; i++) {
+               fprintf(stderr, "\tChecking the node : %s\n", node_handles[i]->name);
+
+               if((0 == strcmp(node_handles[i]->name, "app1node1")) || (0 == strcmp(node_handles[i]->name, "app1node2"))) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               }
+       }
+
+       send_event(MESH_EVENT_COMPLETED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh02/node_sim_corenode1.c b/test/blackbox/test_cases_submesh02/node_sim_corenode1.c
new file mode 100644 (file)
index 0000000..842e5d7
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+    node_sim.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static bool conn_status = false;
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static meshlink_handle_t *mesh = NULL;
+
+static void mesh_send_message_handler(char *destination);
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "corenode1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!memcmp(dat, "Channel Message", len)) {
+               mesh_send_message_handler(channel->node->name);
+
+               if(0 == strcmp("corenode2", channel->node->name)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               }
+       } else if(!memcmp(dat, "failure", 7)) {
+               assert(false);
+       }
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(reachable) {
+               fprintf(stderr, "Node %s became reachable\n", node->name);
+       } else {
+               fprintf(stderr, "Node %s is unreachable\n", node->name);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "corenode1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("corenode2", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+/* channel receive callback */
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "corenode1 got channel request from %s\n", channel->node->name);
+       meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+
+       return true;
+}
+
+void mesh_send_message_handler(char *destination) {
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *target_node = NULL;
+
+       // Open a channel to destination node
+       target_node = meshlink_get_node(mesh, destination);
+       assert(target_node);
+       fprintf(stderr, "corenode1 Sending Channel request to %s at : %lu\n", destination, time(NULL));
+       channel = meshlink_channel_open(mesh, target_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+       int i;
+
+       // Import mesh event handler
+
+       fprintf(stderr, "Mesh node 'corenode1' starting up........\n");
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       setup_signals();
+
+       // Execute test steps
+
+       mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 10));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}
diff --git a/test/blackbox/test_cases_submesh02/node_sim_corenode2.c b/test/blackbox/test_cases_submesh02/node_sim_corenode2.c
new file mode 100644 (file)
index 0000000..a7b4760
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "corenode2 got channel request from %s", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "corenode2 got message from %s as %s", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "corenode2's Channel request has been accepted by corenode1 at : %lu", time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "Node corenode2 became reachable");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in corenode2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "Mesh node 'corenode2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("corenode1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "corenode2 Sending Channel request to corenode1 at : %lu", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 10));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh03/Makefile.am b/test/blackbox/test_cases_submesh03/Makefile.am
new file mode 100644 (file)
index 0000000..7a7004a
--- /dev/null
@@ -0,0 +1,13 @@
+check_PROGRAMS = node_sim_corenode1 node_sim_app1node1 node_sim_app1node2
+
+node_sim_corenode1_SOURCES = node_sim_corenode1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_corenode1_LDADD = ../../../src/libmeshlink.la
+node_sim_corenode1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node1_SOURCES = node_sim_app1node1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node1_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node2_SOURCES = node_sim_app1node2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node2_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node2_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_cases_submesh03/node_sim_app1node1.c b/test/blackbox/test_cases_submesh03/node_sim_app1node1.c
new file mode 100644 (file)
index 0000000..d542357
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node1 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       fprintf(stderr, "\tapp1node1 rejecting channel request from %s at %lu\n", channel->node->name, time(NULL));
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+       char *message = "Channel Message";
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node1 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("corenode1", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node1\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app1node1' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node1 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh03/node_sim_app1node2.c b/test/blackbox/test_cases_submesh03/node_sim_app1node2.c
new file mode 100644 (file)
index 0000000..39d998e
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag app_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node2 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node2 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node2 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else {
+               assert(false);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node2's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       } else if(!strcasecmp(node->name, "app1node1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode app1node1 became reachable\n");
+                       set_sync_flag(&app_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       size_t num_nodes, i;
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+       meshlink_node_t **node_handles = NULL;
+
+       fprintf(stderr, "\tMesh node 'app1node2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node2conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // Open a channel to peer node
+       channel_opened.flag = false;
+       channel_data_recieved.flag = false;
+
+       assert(wait_sync_flag(&app_reachable, 60));
+
+       core_node = meshlink_get_node(mesh, "app1node1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to app1node1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       num_nodes = 0;
+       node_handles = meshlink_get_all_nodes(mesh, NULL, &num_nodes);
+       fprintf(stderr, "\tGot %lu nodes in list with error : %s\n", num_nodes, meshlink_strerror(meshlink_errno));
+       assert(node_handles);
+
+       for(i = 0; i < num_nodes; i++) {
+               fprintf(stderr, "\tChecking the node : %s\n", node_handles[i]->name);
+
+               if(0 == strcmp(node_handles[i]->name, "app2node1")) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               } else if(0 == strcmp(node_handles[i]->name, "app2node2")) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               }
+       }
+
+       send_event(MESH_EVENT_COMPLETED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_cases_submesh03/node_sim_corenode1.c b/test/blackbox/test_cases_submesh03/node_sim_corenode1.c
new file mode 100644 (file)
index 0000000..5648d28
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+    node_sim.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static bool conn_status = false;
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static meshlink_handle_t *mesh = NULL;
+
+static void mesh_send_message_handler(char *destination);
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "corenode1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!memcmp(dat, "Channel Message", len)) {
+               mesh_send_message_handler((char *)channel->node->name);
+
+               if(0 == strcmp("app1node1", channel->node->name)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               }
+       } else if(!memcmp(dat, "failure", 7)) {
+               assert(false);
+       }
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(reachable) {
+               fprintf(stderr, "Node %s became reachable\n", node->name);
+       } else {
+               fprintf(stderr, "Node %s is unreachable\n", node->name);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "corenode1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("app1node1", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+/* channel receive callback */
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "corenode1 got channel request from %s\n", channel->node->name);
+       meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+
+       return true;
+}
+
+void mesh_send_message_handler(char *destination) {
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *target_node = NULL;
+
+       // Open a channel to destination node
+       target_node = meshlink_get_node(mesh, destination);
+       assert(target_node);
+       fprintf(stderr, "corenode1 Sending Channel request to %s at : %lu\n", destination, time(NULL));
+       channel = meshlink_channel_open(mesh, target_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+       int i;
+
+       // Import mesh event handler
+
+       fprintf(stderr, "Mesh node 'corenode1' starting up........\n");
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       setup_signals();
+
+       // Execute test steps
+
+       mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 10));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}
diff --git a/test/blackbox/test_cases_submesh04/Makefile.am b/test/blackbox/test_cases_submesh04/Makefile.am
new file mode 100644 (file)
index 0000000..7a7004a
--- /dev/null
@@ -0,0 +1,13 @@
+check_PROGRAMS = node_sim_corenode1 node_sim_app1node1 node_sim_app1node2
+
+node_sim_corenode1_SOURCES = node_sim_corenode1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_corenode1_LDADD = ../../../src/libmeshlink.la
+node_sim_corenode1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node1_SOURCES = node_sim_app1node1.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node1_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node1_CFLAGS = -D_GNU_SOURCE
+
+node_sim_app1node2_SOURCES = node_sim_app1node2.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_app1node2_LDADD = ../../../src/libmeshlink.la
+node_sim_app1node2_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_cases_submesh04/node_sim_app1node1.c b/test/blackbox/test_cases_submesh04/node_sim_app1node1.c
new file mode 100644 (file)
index 0000000..d542357
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node1 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               fprintf(stderr, "\tapp1node1 accepting channel request from %s at %lu\n", channel->node->name, time(NULL));
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       fprintf(stderr, "\tapp1node1 rejecting channel request from %s at %lu\n", channel->node->name, time(NULL));
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+       char *message = "Channel Message";
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node1 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node2")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("corenode1", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node1\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+
+       fprintf(stderr, "\tMesh node 'app1node1' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node1conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node1 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/test/blackbox/test_cases_submesh04/node_sim_app1node2.c b/test/blackbox/test_cases_submesh04/node_sim_app1node2.c
new file mode 100644 (file)
index 0000000..14729f5
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+static meshlink_handle_t *mesh = NULL;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag start_test = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag app_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static meshlink_channel_t *ch_app1node1 = NULL;
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "\tapp1node2 got channel request from %s\n", channel->node->name);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               fprintf(stderr, "\tapp1node2 got error from %s at %lu\n", channel->node->name, time(NULL));
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "\tapp1node2 got message from %s as %s\n", channel->node->name, data);
+
+       if(!strcmp(channel->node->name, "corenode1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else if(!strcmp(channel->node->name, "app1node1")) {
+               if(!memcmp(dat, "Channel Message", len)) {
+                       ch_app1node1 = channel;
+                       set_sync_flag(&channel_data_recieved, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       } else {
+               assert(false);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "\tapp1node2's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+       set_sync_flag(&channel_opened, true);
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "corenode1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode corenode1 became reachable\n");
+                       set_sync_flag(&peer_reachable, true);
+               }
+       } else if(!strcasecmp(node->name, "app1node1")) {
+               if(reachable) {
+                       fprintf(stderr, "\tNode app1node1 became reachable\n");
+                       set_sync_flag(&app_reachable, true);
+               }
+       }
+
+       return;
+}
+
+void mesh_start_test_handler(int a) {
+       fprintf(stderr, "Starting test in app1node2\n");
+       set_sync_flag(&start_test, true);
+}
+
+int main(int argc, char *argv[]) {
+       size_t num_nodes, i;
+       struct timeval main_loop_wait = { 2, 0 };
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *core_node = NULL;
+       meshlink_node_t **node_handles = NULL;
+       ssize_t retvalue = -1;
+
+       fprintf(stderr, "\tMesh node 'app1node2' starting up........\n");
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGIO, mesh_start_test_handler);
+
+       // Run peer node instance
+
+       mesh = meshlink_open("app1node2conf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 15));
+       send_event(NODE_JOINED);
+
+       while(false == wait_sync_flag(&start_test, 10));
+
+       // Open a channel to peer node
+       core_node = meshlink_get_node(mesh, "corenode1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to corenode1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // Open a channel to peer node
+       channel_opened.flag = false;
+       channel_data_recieved.flag = false;
+
+       assert(wait_sync_flag(&app_reachable, 60));
+
+       core_node = meshlink_get_node(mesh, "app1node1");
+       assert(core_node);
+       fprintf(stderr, "\tapp1node2 Sending Channel request to app1node1 at : %lu\n", time(NULL));
+       channel = meshlink_channel_open(mesh, core_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+       assert(wait_sync_flag(&channel_opened, 15));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 30));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       num_nodes = 0;
+       node_handles = meshlink_get_all_nodes(mesh, NULL, &num_nodes);
+       fprintf(stderr, "\tGot %lu nodes in list with error : %s\n", num_nodes, meshlink_strerror(meshlink_errno));
+       assert(node_handles);
+
+       for(i = 0; i < num_nodes; i++) {
+               fprintf(stderr, "\tChecking the node : %s\n", node_handles[i]->name);
+
+               if(0 == strcmp(node_handles[i]->name, "app2node1")) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               } else if(0 == strcmp(node_handles[i]->name, "app2node2")) {
+                       send_event(SIG_ABORT);
+                       assert(false);
+               }
+       }
+
+       meshlink_node_t *app1_node1 = meshlink_get_node(mesh, "app1node1");
+
+       if(!app1_node1) {
+               send_event(SIG_ABORT);
+               assert(app1_node1);
+       }
+
+       channel_data_recieved.flag = false;
+       meshlink_blacklist(mesh, app1_node1);
+
+       sleep(2);
+
+       meshlink_channel_send(mesh, ch_app1node1, "test", 5);
+
+       wait_sync_flag(&channel_data_recieved, 30);
+
+       if(true == channel_data_recieved.flag) {
+               send_event(SIG_ABORT);
+               assert(false);
+       }
+
+       channel_data_recieved.flag = false;
+       meshlink_whitelist(mesh, app1_node1);
+
+       sleep(2);
+
+       meshlink_channel_send(mesh, ch_app1node1, "Channel Message", strlen("Channel Message"));
+
+       assert(wait_sync_flag(&channel_data_recieved, 60));
+
+       send_event(MESH_EVENT_COMPLETED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_cases_submesh04/node_sim_corenode1.c b/test/blackbox/test_cases_submesh04/node_sim_corenode1.c
new file mode 100644 (file)
index 0000000..d37c54a
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+    node_sim.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static bool conn_status = false;
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+static struct sync_flag channel_data_recieved = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .flag = false};
+
+static meshlink_handle_t *mesh = NULL;
+
+static void mesh_send_message_handler(char *destination);
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       char data[100] = {0};
+
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       memcpy(data, dat, len);
+
+       fprintf(stderr, "corenode1 got message from %s as %s\n", channel->node->name, data);
+
+       if(!memcmp(dat, "Channel Message", len)) {
+               mesh_send_message_handler((char *)channel->node->name);
+
+               if(0 == strcmp("app1node2", channel->node->name)) {
+                       set_sync_flag(&channel_data_recieved, true);
+               }
+       } else if(!memcmp(dat, "failure", 7)) {
+               assert(false);
+       }
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(reachable) {
+               fprintf(stderr, "Node %s became reachable\n", node->name);
+       } else {
+               fprintf(stderr, "Node %s is unreachable\n", node->name);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       char *message = "Channel Message";
+       char *node = (char *)channel->node->name;
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       fprintf(stderr, "corenode1's Channel request has been accepted by %s at : %lu\n", node, time(NULL));
+
+       if(0 == strcmp("app1node2", node)) {
+               set_sync_flag(&channel_opened, true);
+       }
+
+       assert(meshlink_channel_send(mesh, channel, message, strlen(message)) >= 0);
+       return;
+}
+
+/* channel receive callback */
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       fprintf(stderr, "corenode1 got channel request from %s\n", channel->node->name);
+       meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+
+       return true;
+}
+
+void mesh_send_message_handler(char *destination) {
+       meshlink_channel_t *channel = NULL;
+       meshlink_node_t *target_node = NULL;
+
+       // Open a channel to destination node
+       target_node = meshlink_get_node(mesh, destination);
+       assert(target_node);
+       fprintf(stderr, "corenode1 Sending Channel request to %s at : %lu\n", destination, time(NULL));
+       channel = meshlink_channel_open(mesh, target_node, CHANNEL_PORT,
+                                       channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+       int i;
+
+       // Import mesh event handler
+
+       fprintf(stderr, "Mesh node 'corenode1' starting up........\n");
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       setup_signals();
+
+       // Execute test steps
+
+       mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                            "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       send_event(NODE_STARTED);
+
+       assert(wait_sync_flag(&channel_opened, 50));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&channel_data_recieved, 50));
+       send_event(CHANNEL_DATA_RECIEVED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}
index 1517be8a615a81c7fb362998195593e08492e6a4..beb7401f64af698b46fea14e7a7fc1ba4e2e3a73 100644 (file)
 
 #define CMD_LINE_ARG_NODENAME   1
 #define CMD_LINE_ARG_INVITEE    2
-
-void logger_cb(meshlink_handle_t *mesh, meshlink_log_level_t level,
-               const char *text) {
-       (void)mesh;
-       (void)level;
-
-       fprintf(stderr, "meshlink>> %s\n", text);
-}
+#define CMD_LINE_ARG_SUBMESH    3
 
 int main(int argc, char *argv[]) {
        char *invite = NULL;
+       meshlink_submesh_t *s = NULL;
 
        /* Start mesh, generate an invite and print out the invite */
-       /* Set up logging for Meshlink */
-       meshlink_set_log_cb(NULL, MESHLINK_DEBUG, logger_cb);
-
-       /* Create meshlink instance */
-       meshlink_handle_t *mesh = meshlink_open("testconf", argv[1], "node_sim", DEV_CLASS_STATIONARY);
-       assert(mesh);
-
-       /* Set up logging for Meshlink with the newly acquired Mesh Handle */
-       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, logger_cb);
-       meshlink_enable_discovery(mesh, false);
-       assert(meshlink_start(mesh));
-       invite = meshlink_invite_ex(mesh, NULL, argv[2], MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_NUMERIC);
+       meshlink_handle_t *mesh = execute_open(argv[CMD_LINE_ARG_NODENAME], "1");
+       execute_start();
+
+       if(argc > CMD_LINE_ARG_SUBMESH) {
+               s = meshlink_submesh_open(mesh, argv[CMD_LINE_ARG_SUBMESH]);
+       }
+
+       invite = execute_invite(argv[CMD_LINE_ARG_INVITEE], s);
        printf("%s\n", invite);
-       meshlink_close(mesh);
+       execute_close();
 
        return EXIT_SUCCESS;
 }