io: add QIOChannelSocket class
[qemu/kevin.git] / tests / test-io-channel-socket.c
blob194d04387820fa5857c733a46bec7a2a4b2fcf61
1 /*
2 * QEMU I/O channel sockets test
4 * Copyright (c) 2015 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 #include "io/channel-socket.h"
22 #include "io-channel-helpers.h"
23 #ifdef HAVE_IFADDRS_H
24 #include <ifaddrs.h>
25 #endif
27 static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
29 #ifdef HAVE_IFADDRS_H
30 struct ifaddrs *ifaddr = NULL, *ifa;
31 struct addrinfo hints = { 0 };
32 struct addrinfo *ai = NULL;
33 int gaierr;
35 *has_ipv4 = *has_ipv6 = false;
37 if (getifaddrs(&ifaddr) < 0) {
38 g_printerr("Failed to lookup interface addresses: %s\n",
39 strerror(errno));
40 return -1;
43 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
44 if (!ifa->ifa_addr) {
45 continue;
48 if (ifa->ifa_addr->sa_family == AF_INET) {
49 *has_ipv4 = true;
51 if (ifa->ifa_addr->sa_family == AF_INET6) {
52 *has_ipv6 = true;
56 freeifaddrs(ifaddr);
58 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
59 hints.ai_family = AF_INET6;
60 hints.ai_socktype = SOCK_STREAM;
62 gaierr = getaddrinfo("::1", NULL, &hints, &ai);
63 if (gaierr != 0) {
64 if (gaierr == EAI_ADDRFAMILY ||
65 gaierr == EAI_FAMILY ||
66 gaierr == EAI_NONAME) {
67 *has_ipv6 = false;
68 } else {
69 g_printerr("Failed to resolve ::1 address: %s\n",
70 gai_strerror(gaierr));
71 return -1;
75 freeaddrinfo(ai);
77 return 0;
78 #else
79 *has_ipv4 = *has_ipv6 = false;
81 return -1;
82 #endif
86 static void test_io_channel_set_socket_bufs(QIOChannel *src,
87 QIOChannel *dst)
89 int buflen = 64 * 1024;
92 * Make the socket buffers small so that we see
93 * the effects of partial reads/writes
95 setsockopt(((QIOChannelSocket *)src)->fd,
96 SOL_SOCKET, SO_SNDBUF,
97 (char *)&buflen,
98 sizeof(buflen));
100 setsockopt(((QIOChannelSocket *)dst)->fd,
101 SOL_SOCKET, SO_SNDBUF,
102 (char *)&buflen,
103 sizeof(buflen));
107 static void test_io_channel_setup_sync(SocketAddress *listen_addr,
108 SocketAddress *connect_addr,
109 QIOChannel **src,
110 QIOChannel **dst)
112 QIOChannelSocket *lioc;
114 lioc = qio_channel_socket_new();
115 qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort);
117 if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
118 SocketAddress *laddr = qio_channel_socket_get_local_address(
119 lioc, &error_abort);
121 g_free(connect_addr->u.inet->port);
122 connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
124 qapi_free_SocketAddress(laddr);
127 *src = QIO_CHANNEL(qio_channel_socket_new());
128 qio_channel_socket_connect_sync(
129 QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort);
130 qio_channel_set_delay(*src, false);
132 *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
133 g_assert(*dst);
135 test_io_channel_set_socket_bufs(*src, *dst);
137 object_unref(OBJECT(lioc));
141 struct TestIOChannelData {
142 bool err;
143 GMainLoop *loop;
147 static void test_io_channel_complete(Object *src,
148 Error *err,
149 gpointer opaque)
151 struct TestIOChannelData *data = opaque;
152 data->err = err != NULL;
153 g_main_loop_quit(data->loop);
157 static void test_io_channel_setup_async(SocketAddress *listen_addr,
158 SocketAddress *connect_addr,
159 QIOChannel **src,
160 QIOChannel **dst)
162 QIOChannelSocket *lioc;
163 struct TestIOChannelData data;
165 data.loop = g_main_loop_new(g_main_context_default(),
166 TRUE);
168 lioc = qio_channel_socket_new();
169 qio_channel_socket_listen_async(
170 lioc, listen_addr,
171 test_io_channel_complete, &data, NULL);
173 g_main_loop_run(data.loop);
174 g_main_context_iteration(g_main_context_default(), FALSE);
176 g_assert(!data.err);
178 if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
179 SocketAddress *laddr = qio_channel_socket_get_local_address(
180 lioc, &error_abort);
182 g_free(connect_addr->u.inet->port);
183 connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
185 qapi_free_SocketAddress(laddr);
188 *src = QIO_CHANNEL(qio_channel_socket_new());
190 qio_channel_socket_connect_async(
191 QIO_CHANNEL_SOCKET(*src), connect_addr,
192 test_io_channel_complete, &data, NULL);
194 g_main_loop_run(data.loop);
195 g_main_context_iteration(g_main_context_default(), FALSE);
197 g_assert(!data.err);
199 *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
200 g_assert(*dst);
202 qio_channel_set_delay(*src, false);
203 test_io_channel_set_socket_bufs(*src, *dst);
205 object_unref(OBJECT(lioc));
207 g_main_loop_unref(data.loop);
211 static void test_io_channel(bool async,
212 SocketAddress *listen_addr,
213 SocketAddress *connect_addr)
215 QIOChannel *src, *dst;
216 QIOChannelTest *test;
217 if (async) {
218 test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
220 test = qio_channel_test_new();
221 qio_channel_test_run_threads(test, true, src, dst);
222 qio_channel_test_validate(test);
224 object_unref(OBJECT(src));
225 object_unref(OBJECT(dst));
227 test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
229 test = qio_channel_test_new();
230 qio_channel_test_run_threads(test, false, src, dst);
231 qio_channel_test_validate(test);
233 object_unref(OBJECT(src));
234 object_unref(OBJECT(dst));
235 } else {
236 test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
238 test = qio_channel_test_new();
239 qio_channel_test_run_threads(test, true, src, dst);
240 qio_channel_test_validate(test);
242 object_unref(OBJECT(src));
243 object_unref(OBJECT(dst));
245 test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
247 test = qio_channel_test_new();
248 qio_channel_test_run_threads(test, false, src, dst);
249 qio_channel_test_validate(test);
251 object_unref(OBJECT(src));
252 object_unref(OBJECT(dst));
257 static void test_io_channel_ipv4(bool async)
259 SocketAddress *listen_addr = g_new0(SocketAddress, 1);
260 SocketAddress *connect_addr = g_new0(SocketAddress, 1);
262 listen_addr->type = SOCKET_ADDRESS_KIND_INET;
263 listen_addr->u.inet = g_new0(InetSocketAddress, 1);
264 listen_addr->u.inet->host = g_strdup("0.0.0.0");
265 listen_addr->u.inet->port = NULL; /* Auto-select */
267 connect_addr->type = SOCKET_ADDRESS_KIND_INET;
268 connect_addr->u.inet = g_new0(InetSocketAddress, 1);
269 connect_addr->u.inet->host = g_strdup("127.0.0.1");
270 connect_addr->u.inet->port = NULL; /* Filled in later */
272 test_io_channel(async, listen_addr, connect_addr);
274 qapi_free_SocketAddress(listen_addr);
275 qapi_free_SocketAddress(connect_addr);
279 static void test_io_channel_ipv4_sync(void)
281 return test_io_channel_ipv4(false);
285 static void test_io_channel_ipv4_async(void)
287 return test_io_channel_ipv4(true);
291 static void test_io_channel_ipv6(bool async)
293 SocketAddress *listen_addr = g_new0(SocketAddress, 1);
294 SocketAddress *connect_addr = g_new0(SocketAddress, 1);
296 listen_addr->type = SOCKET_ADDRESS_KIND_INET;
297 listen_addr->u.inet = g_new0(InetSocketAddress, 1);
298 listen_addr->u.inet->host = g_strdup("::");
299 listen_addr->u.inet->port = NULL; /* Auto-select */
301 connect_addr->type = SOCKET_ADDRESS_KIND_INET;
302 connect_addr->u.inet = g_new0(InetSocketAddress, 1);
303 connect_addr->u.inet->host = g_strdup("::1");
304 connect_addr->u.inet->port = NULL; /* Filled in later */
306 test_io_channel(async, listen_addr, connect_addr);
308 qapi_free_SocketAddress(listen_addr);
309 qapi_free_SocketAddress(connect_addr);
313 static void test_io_channel_ipv6_sync(void)
315 return test_io_channel_ipv6(false);
319 static void test_io_channel_ipv6_async(void)
321 return test_io_channel_ipv6(true);
325 #ifndef _WIN32
326 static void test_io_channel_unix(bool async)
328 SocketAddress *listen_addr = g_new0(SocketAddress, 1);
329 SocketAddress *connect_addr = g_new0(SocketAddress, 1);
331 #define TEST_SOCKET "test-io-channel-socket.sock"
332 listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
333 listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
334 listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
336 connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
337 connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
338 connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
340 test_io_channel(async, listen_addr, connect_addr);
342 qapi_free_SocketAddress(listen_addr);
343 qapi_free_SocketAddress(connect_addr);
344 unlink(TEST_SOCKET);
348 static void test_io_channel_unix_sync(void)
350 return test_io_channel_unix(false);
354 static void test_io_channel_unix_async(void)
356 return test_io_channel_unix(true);
358 #endif /* _WIN32 */
361 int main(int argc, char **argv)
363 bool has_ipv4, has_ipv6;
365 module_call_init(MODULE_INIT_QOM);
367 g_test_init(&argc, &argv, NULL);
369 /* We're creating actual IPv4/6 sockets, so we should
370 * check if the host running tests actually supports
371 * each protocol to avoid breaking tests on machines
372 * with either IPv4 or IPv6 disabled.
374 if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
375 return 1;
378 if (has_ipv4) {
379 g_test_add_func("/io/channel/socket/ipv4-sync",
380 test_io_channel_ipv4_sync);
381 g_test_add_func("/io/channel/socket/ipv4-async",
382 test_io_channel_ipv4_async);
384 if (has_ipv6) {
385 g_test_add_func("/io/channel/socket/ipv6-sync",
386 test_io_channel_ipv6_sync);
387 g_test_add_func("/io/channel/socket/ipv6-async",
388 test_io_channel_ipv6_async);
391 #ifndef _WIN32
392 g_test_add_func("/io/channel/socket/unix-sync",
393 test_io_channel_unix_sync);
394 g_test_add_func("/io/channel/socket/unix-async",
395 test_io_channel_unix_async);
396 #endif /* _WIN32 */
398 return g_test_run();