From 46079ef8d3adcb693d593cfd362879eb8e4709df Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 22 Sep 2019 22:26:00 +0200 Subject: [PATCH] Test UDP channels. 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 | 2 +- test/Makefile.am | 5 ++ test/channels-udp.c | 197 +++++++++++++++++++++++++++++++++++++++++ test/channels-udp.test | 4 + test/utils.c | 31 ++++--- test/utils.h | 3 + 6 files changed, 228 insertions(+), 14 deletions(-) create mode 100644 test/channels-udp.c create mode 100755 test/channels-udp.test diff --git a/src/utcp b/src/utcp index 275129c9..b92868c9 160000 --- a/src/utcp +++ b/src/utcp @@ -1 +1 @@ -Subproject commit 275129c9d08b2d29529b2f9d039198168aeb52a7 +Subproject commit b92868c9f1e00601c1cb5cccc6dc01fdb79de4ca diff --git a/test/Makefile.am b/test/Makefile.am index f006d132..420c9f24 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 index 00000000..8b9e163f --- /dev/null +++ b/test/channels-udp.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..457f3d1f --- /dev/null +++ b/test/channels-udp.test @@ -0,0 +1,4 @@ +#!/bin/sh + +rm -Rf channels_udp_conf.* +./channels-udp diff --git a/test/utils.c b/test/utils.c index 9a9bd211..21d3b149 100644 --- a/test/utils.c +++ b/test/utils.c @@ -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; diff --git a/test/utils.h b/test/utils.h index a657dffa..dfada2c7 100644 --- a/test/utils.h +++ b/test/utils.h @@ -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;) {\ -- 2.39.2