]> git.meshlink.io Git - meshlink/commitdiff
Test UDP channels.
authorGuus Sliepen <guus@meshlink.io>
Sun, 22 Sep 2019 20:26:00 +0000 (22:26 +0200)
committerGuus Sliepen <guus@meshlink.io>
Sun, 22 Sep 2019 20:26:00 +0000 (22:26 +0200)
Simulate three simultaneous UDP channels transmitting 40 Mbps. Check that
>95% of the packets arrive, and that there is no TCP-like merging or
splitting of packets. This also checks that channels can be opened and
closed like regular TCP channels.

src/utcp
test/Makefile.am
test/channels-udp.c [new file with mode: 0644]
test/channels-udp.test [new file with mode: 0755]
test/utils.c
test/utils.h

index 275129c9d08b2d29529b2f9d039198168aeb52a7..b92868c9f1e00601c1cb5cccc6dc01fdb79de4ca 160000 (submodule)
--- a/src/utcp
+++ b/src/utcp
@@ -1 +1 @@
-Subproject commit 275129c9d08b2d29529b2f9d039198168aeb52a7
+Subproject commit b92868c9f1e00601c1cb5cccc6dc01fdb79de4ca
index f006d13253db075705748afbef51a7c27a28bc94..420c9f241384377ba7e2fdfacfc150a2166a6ec8 100644 (file)
@@ -8,6 +8,7 @@ TESTS = \
        channels-failure.test \
        channels-fork.test \
        channels-no-partial.test \
+       channels-udp.test \
        duplicate.test \
        encrypted.test \
        ephemeral.test \
@@ -36,6 +37,7 @@ check_PROGRAMS = \
        channels-failure \
        channels-fork \
        channels-no-partial \
+       channels-udp \
        duplicate \
        echo-fork \
        encrypted \
@@ -77,6 +79,9 @@ channels_fork_LDADD = ../src/libmeshlink.la
 channels_cornercases_SOURCES = channels-cornercases.c utils.c utils.h
 channels_cornercases_LDADD = ../src/libmeshlink.la
 
+channels_udp_SOURCES = channels-udp.c utils.c utils.h
+channels_udp_LDADD = ../src/libmeshlink.la
+
 duplicate_SOURCES = duplicate.c
 duplicate_LDADD = ../src/libmeshlink.la
 
diff --git a/test/channels-udp.c b/test/channels-udp.c
new file mode 100644 (file)
index 0000000..8b9e163
--- /dev/null
@@ -0,0 +1,197 @@
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "../src/meshlink.h"
+#include "utils.h"
+
+static struct sync_flag accept_flag;
+static struct sync_flag close_flag;
+
+struct client {
+       meshlink_handle_t *mesh;
+       meshlink_channel_t *channel;
+       size_t received;
+};
+
+static void log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
+       static struct timeval tv0;
+       struct timeval tv;
+
+       if(tv0.tv_sec == 0) {
+               gettimeofday(&tv0, NULL);
+       }
+
+       gettimeofday(&tv, NULL);
+       fprintf(stderr, "%u.%.03u ", (unsigned int)(tv.tv_sec - tv0.tv_sec), (unsigned int)tv.tv_usec / 1000);
+
+       if(mesh) {
+               fprintf(stderr, "(%s) ", mesh->name);
+       }
+
+       fprintf(stderr, "[%d] %s\n", level, text);
+}
+
+static void server_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) {
+       (void)data;
+
+       // We expect no data from clients, apart from disconnections.
+       assert(len == 0);
+
+       meshlink_channel_t **c = mesh->priv;
+       int count = 0;
+
+       for(int i = 0; i < 3; i++) {
+               if(c[i] == channel) {
+                       c[i] = NULL;
+                       fprintf(stderr, "server received channel %d closure from %s\n", i, channel->node->name);
+
+                       meshlink_channel_close(mesh, channel);
+               }
+
+               if(c[i]) {
+                       count++;
+               }
+       }
+
+       if(!count) {
+               set_sync_flag(&close_flag, true);
+       }
+}
+
+static void client_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) {
+       (void)channel;
+       (void)data;
+
+       // We expect always the same amount of data from the server.
+       assert(len == 1000);
+       assert(mesh->priv);
+       struct client *client = mesh->priv;
+       client->received += len;
+}
+
+static void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
+       assert(mesh->priv);
+       struct client *client = mesh->priv;
+
+       assert(reachable);
+
+       if(!strcmp(node->name, "server")) {
+               assert(!client->channel);
+               client->channel = meshlink_channel_open_ex(mesh, node, 1, client_receive_cb, NULL, 0, MESHLINK_CHANNEL_UDP);
+               assert(client->channel);
+       }
+}
+
+static bool accept_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *data, size_t len) {
+       (void)data;
+       (void)len;
+
+       assert(port == 1);
+       assert(meshlink_channel_get_flags(mesh, channel) == MESHLINK_CHANNEL_UDP);
+       meshlink_set_channel_receive_cb(mesh, channel, server_receive_cb);
+
+       assert(mesh->priv);
+       meshlink_channel_t **c = mesh->priv;
+
+       for(int i = 0; i < 3; i++) {
+               if(c[i] == NULL) {
+                       fprintf(stderr, "server accepted channel %d from %s\n", i, channel->node->name);
+                       c[i] = channel;
+
+                       if(i == 2) {
+                               set_sync_flag(&accept_flag, true);
+                       }
+
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+int main() {
+       //meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
+
+       // Open two new meshlink instance.
+
+       const char *names[3] = {"foo", "bar", "baz"};
+       struct client clients[3];
+       meshlink_channel_t *channels[3] = {NULL, NULL, NULL};
+       memset(clients, 0, sizeof(clients));
+
+       meshlink_handle_t *server = meshlink_open("channels_udp_conf.0", "server", "channels-udp", DEV_CLASS_BACKBONE);
+       assert(server);
+       meshlink_enable_discovery(server, false);
+       server->priv = channels;
+       meshlink_set_channel_accept_cb(server, accept_cb);
+       assert(meshlink_start(server));
+
+       for(int i = 0; i < 3; i++) {
+               char dir[100];
+               snprintf(dir, sizeof(dir), "channels_udp_conf.%d", i + 1);
+               clients[i].mesh = meshlink_open(dir, names[i], "channels-udp", DEV_CLASS_STATIONARY);
+               assert(clients[i].mesh);
+               clients[i].mesh->priv = &clients[i];
+               meshlink_enable_discovery(clients[i].mesh, false);
+               link_meshlink_pair(server, clients[i].mesh);
+               meshlink_set_node_status_cb(clients[i].mesh, status_cb);
+               assert(meshlink_start(clients[i].mesh));
+       }
+
+       // Wait for all three channels to connect
+
+       assert(wait_sync_flag(&accept_flag, 10));
+
+       for(int i = 0; i < 3; i++) {
+               assert(channels[i]);
+               assert(clients[i].channel);
+       }
+
+       // Stream packets from server to clients for 5 seconds at 40 Mbps (1 kB * 500 Hz)
+
+       char data[1000];
+
+       for(int j = 0; j < 2500; j++) {
+               for(int i = 0; i < 3; i++) {
+                       assert(meshlink_channel_send(server, channels[i], data, sizeof(data)) == sizeof(data));
+               }
+
+               const struct timespec req = {0, 2000000};
+               clock_nanosleep(CLOCK_MONOTONIC, 0, &req, NULL);
+       }
+
+       // Let the clients close the channels
+
+       for(int i = 0; i < 3; i++) {
+               meshlink_channel_close(clients[i].mesh, clients[i].channel);
+               meshlink_set_node_status_cb(clients[i].mesh, NULL);
+       }
+
+       assert(wait_sync_flag(&close_flag, 10));
+
+       // Check that the clients have received (most of) the data
+
+       for(int i = 0; i < 3; i++) {
+               fprintf(stderr, "%s received %zu\n", clients[i].mesh->name, clients[i].received);
+       }
+
+       for(int i = 0; i < 3; i++) {
+               assert(clients[i].received >= 2400000);
+               assert(clients[i].received <= 2500000);
+       }
+
+       // Clean up.
+
+       for(int i = 0; i < 3; i++) {
+               meshlink_close(clients[i].mesh);
+       }
+
+       meshlink_close(server);
+
+       return 0;
+}
diff --git a/test/channels-udp.test b/test/channels-udp.test
new file mode 100755 (executable)
index 0000000..457f3d1
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+rm -Rf channels_udp_conf.*
+./channels-udp
index 9a9bd211fcef7b85a5ee7904062e30f305009134..21d3b149fa06bee7a98d8f02e16be637b4d9fa93 100644 (file)
@@ -41,6 +41,23 @@ bool wait_sync_flag(struct sync_flag *s, int seconds) {
        return s->flag;
 }
 
+void link_meshlink_pair(meshlink_handle_t *a, meshlink_handle_t *b) {
+       // Import and export both side's data
+
+       meshlink_add_address(a, "localhost");
+       meshlink_add_address(b, "localhost");
+
+       char *data = meshlink_export(a);
+       assert(data);
+       assert(meshlink_import(b, data));
+       free(data);
+
+       data = meshlink_export(b);
+       assert(data);
+       assert(meshlink_import(a, data));
+       free(data);
+}
+
 void open_meshlink_pair(meshlink_handle_t **pa, meshlink_handle_t **pb, const char *prefix) {
        // Create two new MeshLink instances
 
@@ -65,19 +82,7 @@ void open_meshlink_pair(meshlink_handle_t **pa, meshlink_handle_t **pb, const ch
        meshlink_enable_discovery(a, false);
        meshlink_enable_discovery(b, false);
 
-       // Import and export both side's data
-
-       meshlink_add_address(a, "localhost");
-
-       char *data = meshlink_export(a);
-       assert(data);
-       assert(meshlink_import(b, data));
-       free(data);
-
-       data = meshlink_export(b);
-       assert(data);
-       assert(meshlink_import(a, data));
-       free(data);
+       link_meshlink_pair(a, b);
 
        *pa = a;
        *pb = b;
index a657dffad6df98305d17eb092a80584497eee8b8..dfada2c70b6e5dab562676690fe3ee18c1aabcc5 100644 (file)
@@ -26,6 +26,9 @@ extern void stop_meshlink_pair(meshlink_handle_t *a, meshlink_handle_t *b);
 /// Stop and cleanup a pair of meshlink instances.
 extern void close_meshlink_pair(meshlink_handle_t *a, meshlink_handle_t *b, const char *prefix);
 
+/// Link two meshlink instances.
+extern void link_meshlink_pair(meshlink_handle_t *a, meshlink_handle_t *b);
+
 #define assert_after(cond, timeout)\
        do {\
                for(int i = 0; i++ <= timeout;) {\