ctdb-tests: Add tests for generic socket I/O
authorAmitay Isaacs <amitay@gmail.com>
Wed, 4 Jan 2017 13:48:32 +0000 (00:48 +1100)
committerMartin Schwenke <martins@samba.org>
Fri, 6 Jan 2017 07:37:28 +0000 (08:37 +0100)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12500

Signed-off-by: Amitay Isaacs <amitay@gmail.com>
Reviewed-by: Martin Schwenke <martin@meltin.net>
ctdb/tests/cunit/sock_io_test_001.sh [new file with mode: 0755]
ctdb/tests/src/sock_io_test.c [new file with mode: 0644]
ctdb/wscript

diff --git a/ctdb/tests/cunit/sock_io_test_001.sh b/ctdb/tests/cunit/sock_io_test_001.sh
new file mode 100755 (executable)
index 0000000..1ead2f3
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+sockpath="${TEST_VAR_DIR}/sock_daemon_test.sock.$$"
+
+ok_null
+
+unit_test sock_io_test "$sockpath"
diff --git a/ctdb/tests/src/sock_io_test.c b/ctdb/tests/src/sock_io_test.c
new file mode 100644 (file)
index 0000000..d0048c1
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+   sock I/O tests
+
+   Copyright (C) Amitay Isaacs  2017
+
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "system/wait.h"
+
+#include <assert.h>
+
+#include "common/sock_io.c"
+
+static int socket_init(const char *sockpath)
+{
+       struct sockaddr_un addr;
+       int fd, ret;
+       size_t len;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+
+       len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
+       assert(len < sizeof(addr.sun_path));
+
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       assert(fd != -1);
+
+       ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+       assert(ret != -1);
+
+       ret = listen(fd, 10);
+       assert(ret != -1);
+
+       return fd;
+}
+
+static void test1_writer(int fd)
+{
+       uint8_t buf[1024];
+       ssize_t nwritten;
+       uint32_t len;
+
+       for (len = 10; len < 1000; len += 10) {
+               int value = len / 10;
+               uint32_t buflen = len + sizeof(uint32_t);
+
+               memset(buf,  value, buflen);
+               memcpy(buf, &buflen, sizeof(uint32_t));
+
+               nwritten = sys_write(fd, buf, buflen);
+               assert(nwritten == buflen);
+       }
+}
+
+struct test1_reader_state {
+       size_t pkt_len;
+       bool done;
+};
+
+static void test1_reader(uint8_t *buf, size_t buflen, void *private_data)
+{
+       struct test1_reader_state *state =
+               (struct test1_reader_state *)private_data;
+
+       if (buflen == 0) {
+               state->done = true;
+               return;
+       }
+
+       assert(buflen == state->pkt_len);
+
+       state->pkt_len += 10;
+}
+
+static void test1(TALLOC_CTX *mem_ctx, const char *sockpath)
+{
+       struct test1_reader_state state;
+       struct tevent_context *ev;
+       struct sock_queue *queue;
+       pid_t pid;
+       int pfd[2], fd, ret;
+       ssize_t n;
+
+       ret = pipe(pfd);
+       assert(ret == 0);
+
+       pid = fork();
+       assert(pid != -1);
+
+       if (pid == 0) {
+               int newfd;
+
+               close(pfd[0]);
+
+               fd = socket_init(sockpath);
+               assert(fd != -1);
+
+               ret = 1;
+               n = sys_write(pfd[1], &ret, sizeof(int));
+               assert(n == sizeof(int));
+
+               newfd = accept(fd, NULL, NULL);
+               assert(newfd != -1);
+
+               test1_writer(newfd);
+               close(newfd);
+               unlink(sockpath);
+
+               exit(0);
+       }
+
+       close(pfd[1]);
+
+       n = sys_read(pfd[0], &ret, sizeof(int));
+       assert(n == sizeof(int));
+       assert(ret == 1);
+
+       close(pfd[0]);
+
+       fd = sock_connect(sockpath);
+       assert(fd != -1);
+
+       ev = tevent_context_init(mem_ctx);
+       assert(ev != NULL);
+
+       state.pkt_len = 10 + sizeof(uint32_t);
+       state.done = false;
+
+       queue = sock_queue_setup(mem_ctx, ev, fd, test1_reader, &state);
+       assert(queue != NULL);
+
+       while (! state.done) {
+               tevent_loop_once(ev);
+       }
+
+       talloc_free(queue);
+       talloc_free(ev);
+
+       pid = wait(&ret);
+       assert(pid != -1);
+}
+
+static void test2_reader(int fd)
+{
+       uint8_t buf[1024];
+       size_t pkt_len = 10 + sizeof(uint32_t);
+       ssize_t n;
+
+       while (1) {
+               n = sys_read(fd, buf, 1024);
+               assert(n != -1);
+
+               if (n == 0) {
+                       return;
+               }
+
+               assert(n == pkt_len);
+               pkt_len += 10;
+       }
+}
+
+static void test2_dummy_reader(uint8_t *buf, size_t buflen,
+                              void *private_data)
+{
+       assert(buflen == -1);
+}
+
+static void test2_writer(struct sock_queue *queue)
+{
+       uint8_t buf[1024];
+       uint32_t len;
+       int ret;
+
+       for (len = 10; len < 1000; len += 10) {
+               int value = len / 10;
+               uint32_t buflen = len + sizeof(uint32_t);
+
+               memset(buf,  value, buflen);
+               memcpy(buf, &buflen, sizeof(uint32_t));
+
+               ret = sock_queue_write(queue, buf, buflen);
+               assert(ret == 0);
+       }
+}
+
+static void test2(TALLOC_CTX *mem_ctx, const char *sockpath)
+{
+       struct tevent_context *ev;
+       struct sock_queue *queue;
+       pid_t pid;
+       int pfd[2], fd, ret;
+       ssize_t n;
+
+       ret = pipe(pfd);
+       assert(ret == 0);
+
+       pid = fork();
+       assert(pid != -1);
+
+       if (pid == 0) {
+               int newfd;
+
+               close(pfd[0]);
+
+               fd = socket_init(sockpath);
+               assert(fd != -1);
+
+               ret = 1;
+               n = sys_write(pfd[1], &ret, sizeof(int));
+               assert(n == sizeof(int));
+
+               newfd = accept(fd, NULL, NULL);
+               assert(newfd != -1);
+
+               test2_reader(newfd);
+               close(newfd);
+               unlink(sockpath);
+
+               exit(0);
+       }
+
+       close(pfd[1]);
+
+       n = sys_read(pfd[0], &ret, sizeof(int));
+       assert(n == sizeof(int));
+       assert(ret == 1);
+
+       close(pfd[0]);
+
+       fd = sock_connect(sockpath);
+       assert(fd != -1);
+
+       ev = tevent_context_init(mem_ctx);
+       assert(ev != NULL);
+
+       queue = sock_queue_setup(mem_ctx, ev, fd, test2_dummy_reader, NULL);
+       assert(queue != NULL);
+
+       test2_writer(queue);
+
+       talloc_free(queue);
+       talloc_free(ev);
+
+       pid = wait(&ret);
+       assert(pid != -1);
+}
+
+int main(int argc, const char **argv)
+{
+       TALLOC_CTX *mem_ctx;
+       const char *sockpath;
+
+       if (argc != 2) {
+               fprintf(stderr, "%s <sockpath>\n", argv[0]);
+               exit(1);
+       }
+
+       sockpath = argv[1];
+
+       mem_ctx = talloc_new(NULL);
+       assert(mem_ctx != NULL);
+
+       test1(mem_ctx, sockpath);
+       test2(mem_ctx, sockpath);
+
+       return 0;
+}
index b951dd69f6c490f9bcad21d95fa358b736842805..4bd7d6692c9830e374587b0dbc845c8de7af9173 100644 (file)
@@ -736,6 +736,7 @@ def build(bld):
         'pidfile_test',
         'run_proc_test',
         'sock_daemon_test',
+        'sock_io_test',
     ]
 
     for target in ctdb_unit_tests: