diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/.gitignore | 8 | ||||
-rw-r--r-- | tests/Makefile | 19 | ||||
-rw-r--r-- | tests/io-channel-helpers.c | 246 | ||||
-rw-r--r-- | tests/io-channel-helpers.h | 42 | ||||
-rw-r--r-- | tests/test-io-channel-buffer.c | 50 | ||||
-rw-r--r-- | tests/test-io-channel-command.c | 129 | ||||
-rw-r--r-- | tests/test-io-channel-file.c | 100 | ||||
-rw-r--r-- | tests/test-io-channel-socket.c | 399 | ||||
-rw-r--r-- | tests/test-io-channel-tls.c | 342 | ||||
-rw-r--r-- | tests/test-io-task.c | 268 |
10 files changed, 1603 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore index 1e55722b6a..77aaba6c2f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -24,6 +24,14 @@ test-cutils test-hbitmap test-int128 test-iov +test-io-channel-buffer +test-io-channel-command +test-io-channel-command.fifo +test-io-channel-file +test-io-channel-file.txt +test-io-channel-socket +test-io-channel-tls +test-io-task test-mul64 test-opts-visitor test-qapi-event.[ch] diff --git a/tests/Makefile b/tests/Makefile index 053c1ae481..6ff4627d0c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -84,6 +84,12 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF) check-unit-y += tests/test-timed-average$(EXESUF) +check-unit-y += tests/test-io-task$(EXESUF) +check-unit-y += tests/test-io-channel-socket$(EXESUF) +check-unit-y += tests/test-io-channel-file$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) +check-unit-y += tests/test-io-channel-command$(EXESUF) +check-unit-y += tests/test-io-channel-buffer$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -381,6 +387,7 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ $(test-qom-obj-y) test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y) +test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y) tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y) tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) @@ -469,6 +476,18 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS) tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) +tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) +tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ + tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ + tests/io-channel-helpers.o $(test-io-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c new file mode 100644 index 0000000000..78d36dd703 --- /dev/null +++ b/tests/io-channel-helpers.c @@ -0,0 +1,246 @@ +/* + * QEMU I/O channel test helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "io-channel-helpers.h" + +struct QIOChannelTest { + QIOChannel *src; + QIOChannel *dst; + bool blocking; + size_t len; + size_t niov; + char *input; + struct iovec *inputv; + char *output; + struct iovec *outputv; + Error *writeerr; + Error *readerr; +}; + + +static void test_skip_iovec(struct iovec **iov, + size_t *niov, + size_t skip, + struct iovec *old) +{ + size_t offset = 0; + size_t i; + + for (i = 0; i < *niov; i++) { + if (skip < (*iov)[i].iov_len) { + old->iov_len = (*iov)[i].iov_len; + old->iov_base = (*iov)[i].iov_base; + + (*iov)[i].iov_len -= skip; + (*iov)[i].iov_base += skip; + break; + } else { + skip -= (*iov)[i].iov_len; + + if (i == 0 && old->iov_base) { + (*iov)[i].iov_len = old->iov_len; + (*iov)[i].iov_base = old->iov_base; + old->iov_len = 0; + old->iov_base = NULL; + } + + offset++; + } + } + + *iov = *iov + offset; + *niov -= offset; +} + + +/* This thread sends all data using iovecs */ +static gpointer test_io_thread_writer(gpointer opaque) +{ + QIOChannelTest *data = opaque; + struct iovec *iov = data->inputv; + size_t niov = data->niov; + struct iovec old = { 0 }; + + qio_channel_set_blocking(data->src, data->blocking, NULL); + + while (niov) { + ssize_t ret; + ret = qio_channel_writev(data->src, + iov, + niov, + &data->writeerr); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + if (data->blocking) { + error_setg(&data->writeerr, + "Unexpected I/O blocking"); + break; + } else { + qio_channel_wait(data->src, + G_IO_OUT); + continue; + } + } else if (ret < 0) { + break; + } else if (ret == 0) { + error_setg(&data->writeerr, + "Unexpected zero length write"); + break; + } + + test_skip_iovec(&iov, &niov, ret, &old); + } + + return NULL; +} + + +/* This thread receives all data using iovecs */ +static gpointer test_io_thread_reader(gpointer opaque) +{ + QIOChannelTest *data = opaque; + struct iovec *iov = data->outputv; + size_t niov = data->niov; + struct iovec old = { 0 }; + + qio_channel_set_blocking(data->dst, data->blocking, NULL); + + while (niov) { + ssize_t ret; + + ret = qio_channel_readv(data->dst, + iov, + niov, + &data->readerr); + + if (ret == QIO_CHANNEL_ERR_BLOCK) { + if (data->blocking) { + error_setg(&data->writeerr, + "Unexpected I/O blocking"); + break; + } else { + qio_channel_wait(data->dst, + G_IO_IN); + continue; + } + } else if (ret < 0) { + break; + } else if (ret == 0) { + break; + } + + test_skip_iovec(&iov, &niov, ret, &old); + } + + return NULL; +} + + +QIOChannelTest *qio_channel_test_new(void) +{ + QIOChannelTest *data = g_new0(QIOChannelTest, 1); + size_t i; + size_t offset; + + + /* We'll send 1 MB of data */ +#define CHUNK_COUNT 250 +#define CHUNK_LEN 4194 + + data->len = CHUNK_COUNT * CHUNK_LEN; + data->input = g_new0(char, data->len); + data->output = g_new0(gchar, data->len); + + /* Fill input with a pattern */ + for (i = 0; i < data->len; i += CHUNK_LEN) { + memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN); + } + + /* We'll split the data across a bunch of IO vecs */ + data->niov = CHUNK_COUNT; + data->inputv = g_new0(struct iovec, data->niov); + data->outputv = g_new0(struct iovec, data->niov); + + for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) { + data->inputv[i].iov_base = data->input + offset; + data->outputv[i].iov_base = data->output + offset; + data->inputv[i].iov_len = CHUNK_LEN; + data->outputv[i].iov_len = CHUNK_LEN; + } + + return data; +} + +void qio_channel_test_run_threads(QIOChannelTest *test, + bool blocking, + QIOChannel *src, + QIOChannel *dst) +{ + GThread *reader, *writer; + + test->src = src; + test->dst = dst; + test->blocking = blocking; + + reader = g_thread_new("reader", + test_io_thread_reader, + test); + writer = g_thread_new("writer", + test_io_thread_writer, + test); + + g_thread_join(reader); + g_thread_join(writer); + + test->dst = test->src = NULL; +} + + +void qio_channel_test_run_writer(QIOChannelTest *test, + QIOChannel *src) +{ + test->src = src; + test_io_thread_writer(test); + test->src = NULL; +} + + +void qio_channel_test_run_reader(QIOChannelTest *test, + QIOChannel *dst) +{ + test->dst = dst; + test_io_thread_reader(test); + test->dst = NULL; +} + + +void qio_channel_test_validate(QIOChannelTest *test) +{ + g_assert_cmpint(memcmp(test->input, + test->output, + test->len), ==, 0); + g_assert(test->readerr == NULL); + g_assert(test->writeerr == NULL); + + g_free(test->inputv); + g_free(test->outputv); + g_free(test->input); + g_free(test->output); + g_free(test); +} diff --git a/tests/io-channel-helpers.h b/tests/io-channel-helpers.h new file mode 100644 index 0000000000..fedc64fd5a --- /dev/null +++ b/tests/io-channel-helpers.h @@ -0,0 +1,42 @@ +/* + * QEMU I/O channel test helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "io/channel.h" + +#ifndef TEST_IO_CHANNEL_HELPERS +#define TEST_IO_CHANNEL_HELPERS + +typedef struct QIOChannelTest QIOChannelTest; + +QIOChannelTest *qio_channel_test_new(void); + +void qio_channel_test_run_threads(QIOChannelTest *test, + bool blocking, + QIOChannel *src, + QIOChannel *dst); + +void qio_channel_test_run_writer(QIOChannelTest *test, + QIOChannel *src); +void qio_channel_test_run_reader(QIOChannelTest *test, + QIOChannel *dst); + +void qio_channel_test_validate(QIOChannelTest *test); + +#endif /* TEST_IO_CHANNEL_HELPERS */ diff --git a/tests/test-io-channel-buffer.c b/tests/test-io-channel-buffer.c new file mode 100644 index 0000000000..6637501b41 --- /dev/null +++ b/tests/test-io-channel-buffer.c @@ -0,0 +1,50 @@ +/* + * QEMU I/O channel buffer test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "io/channel-buffer.h" +#include "io-channel-helpers.h" + + +static void test_io_channel_buf(void) +{ + QIOChannelBuffer *buf; + QIOChannelTest *test; + + buf = qio_channel_buffer_new(0); + + test = qio_channel_test_new(); + qio_channel_test_run_writer(test, QIO_CHANNEL(buf)); + buf->offset = 0; + qio_channel_test_run_reader(test, QIO_CHANNEL(buf)); + qio_channel_test_validate(test); + + object_unref(OBJECT(buf)); +} + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/buf", test_io_channel_buf); + return g_test_run(); +} diff --git a/tests/test-io-channel-command.c b/tests/test-io-channel-command.c new file mode 100644 index 0000000000..03cac36a3f --- /dev/null +++ b/tests/test-io-channel-command.c @@ -0,0 +1,129 @@ +/* + * QEMU I/O channel command test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "io/channel-command.h" +#include "io-channel-helpers.h" + +#ifndef WIN32 +static void test_io_channel_command_fifo(bool async) +{ +#define TEST_FIFO "tests/test-io-channel-command.fifo" + QIOChannel *src, *dst; + QIOChannelTest *test; + char *srcfifo = g_strdup_printf("PIPE:%s,wronly", TEST_FIFO); + char *dstfifo = g_strdup_printf("PIPE:%s,rdonly", TEST_FIFO); + const char *srcargv[] = { + "/bin/socat", "-", srcfifo, NULL, + }; + const char *dstargv[] = { + "/bin/socat", dstfifo, "-", NULL, + }; + + unlink(TEST_FIFO); + if (access("/bin/socat", X_OK) < 0) { + return; /* Pretend success if socat is not present */ + } + if (mkfifo(TEST_FIFO, 0600) < 0) { + abort(); + } + src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv, + O_WRONLY, + &error_abort)); + dst = QIO_CHANNEL(qio_channel_command_new_spawn(dstargv, + O_RDONLY, + &error_abort)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + + g_free(srcfifo); + g_free(dstfifo); + unlink(TEST_FIFO); +} + + +static void test_io_channel_command_fifo_async(void) +{ + test_io_channel_command_fifo(true); +} + +static void test_io_channel_command_fifo_sync(void) +{ + test_io_channel_command_fifo(false); +} + + +static void test_io_channel_command_echo(bool async) +{ + QIOChannel *ioc; + QIOChannelTest *test; + const char *socatargv[] = { + "/bin/socat", "-", "-", NULL, + }; + + if (access("/bin/socat", X_OK) < 0) { + return; /* Pretend success if socat is not present */ + } + + ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv, + O_RDWR, + &error_abort)); + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, ioc, ioc); + qio_channel_test_validate(test); + + object_unref(OBJECT(ioc)); +} + + +static void test_io_channel_command_echo_async(void) +{ + test_io_channel_command_echo(true); +} + +static void test_io_channel_command_echo_sync(void) +{ + test_io_channel_command_echo(false); +} +#endif + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + +#ifndef WIN32 + g_test_add_func("/io/channel/command/fifo/sync", + test_io_channel_command_fifo_sync); + g_test_add_func("/io/channel/command/fifo/async", + test_io_channel_command_fifo_async); + g_test_add_func("/io/channel/command/echo/sync", + test_io_channel_command_echo_sync); + g_test_add_func("/io/channel/command/echo/async", + test_io_channel_command_echo_async); +#endif + + return g_test_run(); +} diff --git a/tests/test-io-channel-file.c b/tests/test-io-channel-file.c new file mode 100644 index 0000000000..f276a32de0 --- /dev/null +++ b/tests/test-io-channel-file.c @@ -0,0 +1,100 @@ +/* + * QEMU I/O channel file test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "io/channel-file.h" +#include "io-channel-helpers.h" + + +static void test_io_channel_file(void) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + +#define TEST_FILE "tests/test-io-channel-file.txt" + unlink(TEST_FILE); + src = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600, + &error_abort)); + dst = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, + O_RDONLY | O_BINARY, 0, + &error_abort)); + + test = qio_channel_test_new(); + qio_channel_test_run_writer(test, src); + qio_channel_test_run_reader(test, dst); + qio_channel_test_validate(test); + + unlink(TEST_FILE); + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + + +#ifndef _WIN32 +static void test_io_channel_pipe(bool async) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + int fd[2]; + + if (pipe(fd) < 0) { + perror("pipe"); + abort(); + } + + src = QIO_CHANNEL(qio_channel_file_new_fd(fd[1])); + dst = QIO_CHANNEL(qio_channel_file_new_fd(fd[0])); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + + +static void test_io_channel_pipe_async(void) +{ + test_io_channel_pipe(true); +} + +static void test_io_channel_pipe_sync(void) +{ + test_io_channel_pipe(false); +} +#endif /* ! _WIN32 */ + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/file", test_io_channel_file); +#ifndef _WIN32 + g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync); + g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async); +#endif + return g_test_run(); +} diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c new file mode 100644 index 0000000000..194d043878 --- /dev/null +++ b/tests/test-io-channel-socket.c @@ -0,0 +1,399 @@ +/* + * QEMU I/O channel sockets test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "io/channel-socket.h" +#include "io-channel-helpers.h" +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +static int check_protocol_support(bool *has_ipv4, bool *has_ipv6) +{ +#ifdef HAVE_IFADDRS_H + struct ifaddrs *ifaddr = NULL, *ifa; + struct addrinfo hints = { 0 }; + struct addrinfo *ai = NULL; + int gaierr; + + *has_ipv4 = *has_ipv6 = false; + + if (getifaddrs(&ifaddr) < 0) { + g_printerr("Failed to lookup interface addresses: %s\n", + strerror(errno)); + return -1; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET) { + *has_ipv4 = true; + } + if (ifa->ifa_addr->sa_family == AF_INET6) { + *has_ipv6 = true; + } + } + + freeifaddrs(ifaddr); + + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + + gaierr = getaddrinfo("::1", NULL, &hints, &ai); + if (gaierr != 0) { + if (gaierr == EAI_ADDRFAMILY || + gaierr == EAI_FAMILY || + gaierr == EAI_NONAME) { + *has_ipv6 = false; + } else { + g_printerr("Failed to resolve ::1 address: %s\n", + gai_strerror(gaierr)); + return -1; + } + } + + freeaddrinfo(ai); + + return 0; +#else + *has_ipv4 = *has_ipv6 = false; + + return -1; +#endif +} + + +static void test_io_channel_set_socket_bufs(QIOChannel *src, + QIOChannel *dst) +{ + int buflen = 64 * 1024; + + /* + * Make the socket buffers small so that we see + * the effects of partial reads/writes + */ + setsockopt(((QIOChannelSocket *)src)->fd, + SOL_SOCKET, SO_SNDBUF, + (char *)&buflen, + sizeof(buflen)); + + setsockopt(((QIOChannelSocket *)dst)->fd, + SOL_SOCKET, SO_SNDBUF, + (char *)&buflen, + sizeof(buflen)); +} + + +static void test_io_channel_setup_sync(SocketAddress *listen_addr, + SocketAddress *connect_addr, + QIOChannel **src, + QIOChannel **dst) +{ + QIOChannelSocket *lioc; + + lioc = qio_channel_socket_new(); + qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort); + + if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) { + SocketAddress *laddr = qio_channel_socket_get_local_address( + lioc, &error_abort); + + g_free(connect_addr->u.inet->port); + connect_addr->u.inet->port = g_strdup(laddr->u.inet->port); + + qapi_free_SocketAddress(laddr); + } + + *src = QIO_CHANNEL(qio_channel_socket_new()); + qio_channel_socket_connect_sync( + QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort); + qio_channel_set_delay(*src, false); + + *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); + g_assert(*dst); + + test_io_channel_set_socket_bufs(*src, *dst); + + object_unref(OBJECT(lioc)); +} + + +struct TestIOChannelData { + bool err; + GMainLoop *loop; +}; + + +static void test_io_channel_complete(Object *src, + Error *err, + gpointer opaque) +{ + struct TestIOChannelData *data = opaque; + data->err = err != NULL; + g_main_loop_quit(data->loop); +} + + +static void test_io_channel_setup_async(SocketAddress *listen_addr, + SocketAddress *connect_addr, + QIOChannel **src, + QIOChannel **dst) +{ + QIOChannelSocket *lioc; + struct TestIOChannelData data; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + + lioc = qio_channel_socket_new(); + qio_channel_socket_listen_async( + lioc, listen_addr, + test_io_channel_complete, &data, NULL); + + g_main_loop_run(data.loop); + g_main_context_iteration(g_main_context_default(), FALSE); + + g_assert(!data.err); + + if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) { + SocketAddress *laddr = qio_channel_socket_get_local_address( + lioc, &error_abort); + + g_free(connect_addr->u.inet->port); + connect_addr->u.inet->port = g_strdup(laddr->u.inet->port); + + qapi_free_SocketAddress(laddr); + } + + *src = QIO_CHANNEL(qio_channel_socket_new()); + + qio_channel_socket_connect_async( + QIO_CHANNEL_SOCKET(*src), connect_addr, + test_io_channel_complete, &data, NULL); + + g_main_loop_run(data.loop); + g_main_context_iteration(g_main_context_default(), FALSE); + + g_assert(!data.err); + + *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); + g_assert(*dst); + + qio_channel_set_delay(*src, false); + test_io_channel_set_socket_bufs(*src, *dst); + + object_unref(OBJECT(lioc)); + + g_main_loop_unref(data.loop); +} + + +static void test_io_channel(bool async, + SocketAddress *listen_addr, + SocketAddress *connect_addr) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + if (async) { + test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + + test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + } else { + test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + + test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + } +} + + +static void test_io_channel_ipv4(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + + listen_addr->type = SOCKET_ADDRESS_KIND_INET; + listen_addr->u.inet = g_new0(InetSocketAddress, 1); + listen_addr->u.inet->host = g_strdup("0.0.0.0"); + listen_addr->u.inet->port = NULL; /* Auto-select */ + + connect_addr->type = SOCKET_ADDRESS_KIND_INET; + connect_addr->u.inet = g_new0(InetSocketAddress, 1); + connect_addr->u.inet->host = g_strdup("127.0.0.1"); + connect_addr->u.inet->port = NULL; /* Filled in later */ + + test_io_channel(async, listen_addr, connect_addr); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); +} + + +static void test_io_channel_ipv4_sync(void) +{ + return test_io_channel_ipv4(false); +} + + +static void test_io_channel_ipv4_async(void) +{ + return test_io_channel_ipv4(true); +} + + +static void test_io_channel_ipv6(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + + listen_addr->type = SOCKET_ADDRESS_KIND_INET; + listen_addr->u.inet = g_new0(InetSocketAddress, 1); + listen_addr->u.inet->host = g_strdup("::"); + listen_addr->u.inet->port = NULL; /* Auto-select */ + + connect_addr->type = SOCKET_ADDRESS_KIND_INET; + connect_addr->u.inet = g_new0(InetSocketAddress, 1); + connect_addr->u.inet->host = g_strdup("::1"); + connect_addr->u.inet->port = NULL; /* Filled in later */ + + test_io_channel(async, listen_addr, connect_addr); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); +} + + +static void test_io_channel_ipv6_sync(void) +{ + return test_io_channel_ipv6(false); +} + + +static void test_io_channel_ipv6_async(void) +{ + return test_io_channel_ipv6(true); +} + + +#ifndef _WIN32 +static void test_io_channel_unix(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + +#define TEST_SOCKET "test-io-channel-socket.sock" + listen_addr->type = SOCKET_ADDRESS_KIND_UNIX; + listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1); + listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET); + + connect_addr->type = SOCKET_ADDRESS_KIND_UNIX; + connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1); + connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET); + + test_io_channel(async, listen_addr, connect_addr); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); + unlink(TEST_SOCKET); +} + + +static void test_io_channel_unix_sync(void) +{ + return test_io_channel_unix(false); +} + + +static void test_io_channel_unix_async(void) +{ + return test_io_channel_unix(true); +} +#endif /* _WIN32 */ + + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + /* We're creating actual IPv4/6 sockets, so we should + * check if the host running tests actually supports + * each protocol to avoid breaking tests on machines + * with either IPv4 or IPv6 disabled. + */ + if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + return 1; + } + + if (has_ipv4) { + g_test_add_func("/io/channel/socket/ipv4-sync", + test_io_channel_ipv4_sync); + g_test_add_func("/io/channel/socket/ipv4-async", + test_io_channel_ipv4_async); + } + if (has_ipv6) { + g_test_add_func("/io/channel/socket/ipv6-sync", + test_io_channel_ipv6_sync); + g_test_add_func("/io/channel/socket/ipv6-async", + test_io_channel_ipv6_async); + } + +#ifndef _WIN32 + g_test_add_func("/io/channel/socket/unix-sync", + test_io_channel_unix_sync); + g_test_add_func("/io/channel/socket/unix-async", + test_io_channel_unix_async); +#endif /* _WIN32 */ + + return g_test_run(); +} diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c new file mode 100644 index 0000000000..3c11a5097c --- /dev/null +++ b/tests/test-io-channel-tls.c @@ -0,0 +1,342 @@ +/* + * QEMU I/O channel TLS test + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#include <stdlib.h> +#include <fcntl.h> + +#include "config-host.h" +#include "crypto-tls-x509-helpers.h" +#include "io/channel-tls.h" +#include "io/channel-socket.h" +#include "io-channel-helpers.h" +#include "crypto/tlscredsx509.h" +#include "qemu/acl.h" +#include "qom/object_interfaces.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-io-channel-tls-work/" +#define KEYFILE WORKDIR "key-ctx.pem" + +struct QIOChannelTLSTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + +struct QIOChannelTLSHandshakeData { + bool finished; + bool failed; +}; + +static void test_tls_handshake_done(Object *source, + Error *err, + gpointer opaque) +{ + struct QIOChannelTLSHandshakeData *data = opaque; + + data->finished = true; + data->failed = err != NULL; +} + + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + errp, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + /* We skip initial sanity checks here because we + * want to make sure that problems are being + * detected at the TLS session validation stage, + * and the test-crypto-tlscreds test already + * validate the sanity check code. + */ + "sanity-check", "no", + NULL + ); + + if (*errp) { + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + + +/* + * This tests validation checking of peer certificates + * + * This is replicating the checks that are done for an + * active TLS session after handshake completes. To + * simulate that we create our TLS contexts, skipping + * sanity checks. When then get a socketpair, and + * initiate a TLS session across them. Finally do + * do actual cert validation tests + */ +static void test_io_channel_tls(const void *opaque) +{ + struct QIOChannelTLSTestData *data = + (struct QIOChannelTLSTestData *)opaque; + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QIOChannelTLS *clientChanTLS; + QIOChannelTLS *serverChanTLS; + QIOChannelSocket *clientChanSock; + QIOChannelSocket *serverChanSock; + qemu_acl *acl; + const char * const *wildcards; + int channel[2]; + struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; + struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; + Error *err = NULL; + QIOChannelTest *test; + GMainContext *mainloop; + + /* We'll use this for our fake client-server connection */ + g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); + +#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" +#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" + mkdir(CLIENT_CERT_DIR, 0700); + mkdir(SERVER_CERT_DIR, 0700); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + g_assert(link(data->servercacrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->servercrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + g_assert(link(KEYFILE, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + + g_assert(link(data->clientcacrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->clientcrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + g_assert(link(KEYFILE, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + + clientCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + CLIENT_CERT_DIR, + &err); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + SERVER_CERT_DIR, + &err); + g_assert(serverCreds != NULL); + + acl = qemu_acl_init("channeltlsacl"); + qemu_acl_reset(acl); + wildcards = data->wildcards; + while (wildcards && *wildcards) { + qemu_acl_append(acl, 0, *wildcards); + wildcards++; + } + + clientChanSock = qio_channel_socket_new_fd( + channel[0], &err); + g_assert(clientChanSock != NULL); + serverChanSock = qio_channel_socket_new_fd( + channel[1], &err); + g_assert(serverChanSock != NULL); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL); + qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL); + + /* Now the real part of the test, setup the sessions */ + clientChanTLS = qio_channel_tls_new_client( + QIO_CHANNEL(clientChanSock), clientCreds, + data->hostname, &err); + g_assert(clientChanTLS != NULL); + + serverChanTLS = qio_channel_tls_new_server( + QIO_CHANNEL(serverChanSock), serverCreds, + "channeltlsacl", &err); + g_assert(serverChanTLS != NULL); + + qio_channel_tls_handshake(clientChanTLS, + test_tls_handshake_done, + &clientHandshake, + NULL); + qio_channel_tls_handshake(serverChanTLS, + test_tls_handshake_done, + &serverHandshake, + NULL); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + mainloop = g_main_context_default(); + do { + g_main_context_iteration(mainloop, TRUE); + } while (!clientHandshake.finished && + !serverHandshake.finished); + + g_assert(clientHandshake.failed == data->expectClientFail); + g_assert(serverHandshake.failed == data->expectServerFail); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, + QIO_CHANNEL(clientChanTLS), + QIO_CHANNEL(serverChanTLS)); + qio_channel_test_validate(test); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, + QIO_CHANNEL(clientChanTLS), + QIO_CHANNEL(serverChanTLS)); + qio_channel_test_validate(test); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + rmdir(CLIENT_CERT_DIR); + rmdir(SERVER_CERT_DIR); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + object_unref(OBJECT(serverChanTLS)); + object_unref(OBJECT(clientChanTLS)); + + object_unref(OBJECT(serverChanSock)); + object_unref(OBJECT(clientChanSock)); + + close(channel[0]); + close(channel[1]); +} + + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TEST_CHANNEL(name, caCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QIOChannelTLSTestData name = { \ + caCrt, caCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qio/channel/tls/" # name, \ + &name, test_io_channel_tls); + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + const char *const wildcards[] = { + "C=UK,CN=qemu*", + NULL, + }; + TEST_CHANNEL(basic, cacertreq.filename, servercertreq.filename, + clientcertreq.filename, false, false, + "qemu.org", wildcards); + + ret = g_test_run(); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&cacertreq); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/test-io-task.c b/tests/test-io-task.c new file mode 100644 index 0000000000..3344382c7f --- /dev/null +++ b/tests/test-io-task.c @@ -0,0 +1,268 @@ +/* + * QEMU I/O task tests + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <glib.h> + +#include "io/task.h" + +#define TYPE_DUMMY "qemu:dummy" + +typedef struct DummyObject DummyObject; +typedef struct DummyObjectClass DummyObjectClass; + +struct DummyObject { + Object parent; +}; + +struct DummyObjectClass { + ObjectClass parent; +}; + +static const TypeInfo dummy_info = { + .parent = TYPE_OBJECT, + .name = TYPE_DUMMY, + .instance_size = sizeof(DummyObject), + .class_size = sizeof(DummyObjectClass), +}; + +struct TestTaskData { + Object *source; + Error *err; + bool freed; +}; + + +static void task_callback(Object *source, + Error *err, + gpointer opaque) +{ + struct TestTaskData *data = opaque; + + data->source = source; + data->err = err; +} + + +static void test_task_complete(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + Object *src; + struct TestTaskData data = { NULL, NULL, false }; + + task = qio_task_new(obj, task_callback, &data, NULL); + src = qio_task_get_source(task); + + qio_task_complete(task); + + g_assert(obj == src); + + object_unref(obj); + object_unref(src); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + g_assert(data.freed == false); +} + + +static void task_data_free(gpointer opaque) +{ + struct TestTaskData *data = opaque; + + data->freed = true; +} + + +static void test_task_data_free(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestTaskData data = { NULL, NULL, false }; + + task = qio_task_new(obj, task_callback, &data, task_data_free); + + qio_task_complete(task); + + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + g_assert(data.freed == true); +} + + +static void test_task_error(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestTaskData data = { NULL, NULL, false }; + Error *err = NULL; + + task = qio_task_new(obj, task_callback, &data, NULL); + + error_setg(&err, "Some error"); + + qio_task_abort(task, err); + + error_free(err); + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == err); + g_assert(data.freed == false); + +} + + +struct TestThreadWorkerData { + Object *source; + Error *err; + bool fail; + GThread *worker; + GThread *complete; + GMainLoop *loop; +}; + +static int test_task_thread_worker(QIOTask *task, + Error **errp, + gpointer opaque) +{ + struct TestThreadWorkerData *data = opaque; + + data->worker = g_thread_self(); + + if (data->fail) { + error_setg(errp, "Testing fail"); + return -1; + } + + return 0; +} + + +static void test_task_thread_callback(Object *source, + Error *err, + gpointer opaque) +{ + struct TestThreadWorkerData *data = opaque; + + data->source = source; + data->err = err; + + data->complete = g_thread_self(); + + g_main_loop_quit(data->loop); +} + + +static void test_task_thread_complete(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestThreadWorkerData data = { 0 }; + GThread *self; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + + task = qio_task_new(obj, + test_task_thread_callback, + &data, + NULL); + + qio_task_run_in_thread(task, + test_task_thread_worker, + &data, + NULL); + + g_main_loop_run(data.loop); + + g_main_loop_unref(data.loop); + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + + self = g_thread_self(); + + /* Make sure the test_task_thread_worker actually got + * run in a different thread */ + g_assert(data.worker != self); + + /* And that the test_task_thread_callback got rnu in + * the main loop thread (ie this one) */ + g_assert(data.complete == self); +} + + +static void test_task_thread_error(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestThreadWorkerData data = { 0 }; + GThread *self; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + data.fail = true; + + task = qio_task_new(obj, + test_task_thread_callback, + &data, + NULL); + + qio_task_run_in_thread(task, + test_task_thread_worker, + &data, + NULL); + + g_main_loop_run(data.loop); + + g_main_loop_unref(data.loop); + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err != NULL); + + self = g_thread_self(); + + /* Make sure the test_task_thread_worker actually got + * run in a different thread */ + g_assert(data.worker != self); + + /* And that the test_task_thread_callback got rnu in + * the main loop thread (ie this one) */ + g_assert(data.complete == self); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + module_call_init(MODULE_INIT_QOM); + type_register_static(&dummy_info); + g_test_add_func("/crypto/task/complete", test_task_complete); + g_test_add_func("/crypto/task/datafree", test_task_data_free); + g_test_add_func("/crypto/task/error", test_task_error); + g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete); + g_test_add_func("/crypto/task/thread_error", test_task_thread_error); + return g_test_run(); +} |