Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright (C) 2010 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : */
20 : :
21 : : #include "config.h"
22 : :
23 : : #include <gio/gio.h>
24 : : #include <glib/gstdio.h>
25 : :
26 : : #ifdef G_OS_UNIX
27 : : #include <fcntl.h>
28 : : #ifdef HAVE_OPENPTY
29 : : #include <pty.h>
30 : : #endif
31 : : #ifdef HAVE_UNISTD_H
32 : : #include <unistd.h>
33 : : #endif
34 : : #include <gio/gunixinputstream.h>
35 : : #include <gio/gunixoutputstream.h>
36 : : #endif
37 : :
38 : : /* openpty() is non-standard and might not be available on all kernels
39 : : * and libc implementations, but glibc on Linux definitely has it */
40 : : #if defined(__linux__) && defined(__GNUC__) && !defined(HAVE_OPENPTY)
41 : : #error Should have been able to find openpty on GNU/Linux
42 : : #endif
43 : :
44 : : static gboolean
45 : 5 : poll_source_callback (GPollableInputStream *input,
46 : : gpointer user_data)
47 : : {
48 : 5 : GError *error = NULL;
49 : : char buf[2];
50 : : gssize nread;
51 : 5 : gboolean *success = user_data;
52 : :
53 : 5 : g_assert_true (g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (input)));
54 : :
55 : 5 : nread = g_pollable_input_stream_read_nonblocking (input, buf, 2, NULL, &error);
56 : 5 : g_assert_no_error (error);
57 : 5 : g_assert_cmpint (nread, ==, 2);
58 : 5 : g_assert_cmpstr (buf, ==, "x");
59 : 5 : g_assert_false (g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (input)));
60 : :
61 : 5 : *success = TRUE;
62 : 5 : return G_SOURCE_REMOVE;
63 : : }
64 : :
65 : : static gboolean
66 : 10 : check_source_not_readable_callback (gpointer user_data)
67 : : {
68 : 10 : GPollableInputStream *in = G_POLLABLE_INPUT_STREAM (user_data);
69 : :
70 : 10 : g_assert_false (g_pollable_input_stream_is_readable (in));
71 : :
72 : 10 : return G_SOURCE_REMOVE;
73 : : }
74 : :
75 : : typedef struct
76 : : {
77 : : GPollableInputStream *in; /* (unowned) */
78 : : GOutputStream *out; /* (unowned) */
79 : : } Streams;
80 : :
81 : : static gboolean
82 : 5 : write_callback (gpointer user_data)
83 : : {
84 : 5 : Streams *streams = user_data;
85 : 5 : const char *buf = "x";
86 : : gssize nwrote;
87 : 5 : GError *error = NULL;
88 : :
89 : 5 : g_assert_true (g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (streams->out)));
90 : :
91 : 5 : nwrote = g_output_stream_write (streams->out, buf, 2, NULL, &error);
92 : 5 : g_assert_no_error (error);
93 : 5 : g_assert_cmpint (nwrote, ==, 2);
94 : 5 : g_assert_true (g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (streams->out)));
95 : :
96 : : /* Wait for the pipe to propagate the write for sockets. */
97 : 5 : while (!g_pollable_input_stream_is_readable (streams->in));
98 : 5 : g_assert_true (g_pollable_input_stream_is_readable (streams->in));
99 : :
100 : 5 : return G_SOURCE_REMOVE;
101 : : }
102 : :
103 : : static gboolean
104 : 5 : quit_callback (gpointer user_data)
105 : : {
106 : 5 : GMainLoop *loop = user_data;
107 : :
108 : 5 : g_main_loop_quit (loop);
109 : :
110 : 5 : return G_SOURCE_REMOVE;
111 : : }
112 : :
113 : : static void
114 : 5 : test_streams (GPollableInputStream *in,
115 : : GOutputStream *out)
116 : : {
117 : : gboolean readable;
118 : 5 : GError *error = NULL;
119 : : char buf[1];
120 : : gssize nread;
121 : : GSource *poll_source;
122 : 5 : gboolean success = FALSE;
123 : : Streams streams;
124 : 5 : GMainLoop *loop = NULL;
125 : :
126 : 5 : g_assert_true (g_pollable_input_stream_can_poll (in));
127 : 5 : g_assert_true (g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (out)));
128 : :
129 : 5 : readable = g_pollable_input_stream_is_readable (in);
130 : 5 : g_assert_false (readable);
131 : :
132 : 5 : nread = g_pollable_input_stream_read_nonblocking (in, buf, 1, NULL, &error);
133 : 5 : g_assert_cmpint (nread, ==, -1);
134 : 5 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK);
135 : 5 : g_clear_error (&error);
136 : :
137 : : /* Create 4 sources, in decreasing order of priority:
138 : : * 1. poll source on @in
139 : : * 2. idle source that checks if @in is readable once
140 : : * (it won't be) and then removes itself
141 : : * 3. idle source that writes a byte to @out, checks that
142 : : * @in is now readable, and removes itself
143 : : * 4. idle source that checks if @in is readable once
144 : : * (it won't be, since the poll source will fire before
145 : : * this one does) and then quits the loop.
146 : : *
147 : : * If the poll source triggers before it should, then it will get a
148 : : * %G_IO_ERROR_WOULD_BLOCK, and if check() fails in either
149 : : * direction, we will catch it at some point.
150 : : */
151 : :
152 : 5 : poll_source = g_pollable_input_stream_create_source (in, NULL);
153 : 5 : g_source_set_priority (poll_source, 1);
154 : 5 : g_source_set_callback (poll_source, (GSourceFunc) poll_source_callback, &success, NULL);
155 : 5 : g_source_attach (poll_source, NULL);
156 : 5 : g_source_unref (poll_source);
157 : :
158 : 5 : streams.in = in;
159 : 5 : streams.out = out;
160 : 5 : loop = g_main_loop_new (NULL, FALSE);
161 : :
162 : 5 : g_idle_add_full (2, check_source_not_readable_callback, in, NULL);
163 : 5 : g_idle_add_full (3, write_callback, &streams, NULL);
164 : 5 : g_idle_add_full (4, check_source_not_readable_callback, in, NULL);
165 : 5 : g_idle_add_full (5, quit_callback, loop, NULL);
166 : :
167 : 5 : g_main_loop_run (loop);
168 : 5 : g_main_loop_unref (loop);
169 : :
170 : 5 : g_assert_cmpint (success, ==, TRUE);
171 : 5 : }
172 : :
173 : : #ifdef G_OS_UNIX
174 : :
175 : : #define g_assert_not_pollable(fd) \
176 : : G_STMT_START { \
177 : : GPollableInputStream *in = NULL; \
178 : : GOutputStream *out = NULL; \
179 : : \
180 : : in = G_POLLABLE_INPUT_STREAM (g_unix_input_stream_new (fd, FALSE)); \
181 : : out = g_unix_output_stream_new (fd, FALSE); \
182 : : \
183 : : g_assert_false (g_pollable_input_stream_can_poll (in)); \
184 : : g_assert_false (g_pollable_output_stream_can_poll ( \
185 : : G_POLLABLE_OUTPUT_STREAM (out))); \
186 : : \
187 : : g_clear_object (&in); \
188 : : g_clear_object (&out); \
189 : : } G_STMT_END
190 : :
191 : : #define g_assert_pollable(fd) \
192 : : G_STMT_START { \
193 : : GPollableInputStream *in = NULL; \
194 : : GOutputStream *out = NULL; \
195 : : \
196 : : in = G_POLLABLE_INPUT_STREAM (g_unix_input_stream_new (fd, FALSE)); \
197 : : out = g_unix_output_stream_new (fd, FALSE); \
198 : : \
199 : : g_assert_true (g_pollable_input_stream_can_poll (in)); \
200 : : g_assert_true (g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (out))); \
201 : : \
202 : : g_clear_object (&in); \
203 : : g_clear_object (&out); \
204 : : } G_STMT_END
205 : :
206 : : static void
207 : 1 : test_pollable_unix_pipe (void)
208 : : {
209 : : int pipefds[2], status;
210 : 1 : GPollableInputStream *in = NULL;
211 : 1 : GOutputStream *out = NULL;
212 : :
213 : 1 : g_test_summary ("Test that pipes are considered pollable, just like sockets");
214 : :
215 : 1 : status = pipe (pipefds);
216 : 1 : g_assert_cmpint (status, ==, 0);
217 : :
218 : 1 : in = G_POLLABLE_INPUT_STREAM (g_unix_input_stream_new (pipefds[0], TRUE));
219 : 1 : out = g_unix_output_stream_new (pipefds[1], TRUE);
220 : :
221 : 1 : test_streams (in, out);
222 : :
223 : 1 : g_object_unref (in);
224 : 1 : g_object_unref (out);
225 : 1 : }
226 : :
227 : : static void
228 : 1 : test_pollable_unix_pty (void)
229 : : {
230 : : #ifdef HAVE_OPENPTY
231 : 1 : GPollableInputStream *in = NULL;
232 : 1 : GOutputStream *out = NULL;
233 : : int a, b, status;
234 : : #endif
235 : :
236 : 1 : g_test_summary ("Test that PTYs are considered pollable");
237 : :
238 : : #ifdef HAVE_OPENPTY
239 : 1 : status = openpty (&a, &b, NULL, NULL, NULL);
240 : :
241 : 1 : if (status == -1)
242 : : {
243 : 0 : g_test_skip ("Unable to open PTY");
244 : 0 : return;
245 : : }
246 : :
247 : 1 : in = G_POLLABLE_INPUT_STREAM (g_unix_input_stream_new (a, TRUE));
248 : 1 : out = g_unix_output_stream_new (b, TRUE);
249 : :
250 : 1 : test_streams (in, out);
251 : :
252 : 1 : g_object_unref (in);
253 : 1 : g_object_unref (out);
254 : :
255 : 1 : close (a);
256 : 1 : close (b);
257 : : #else
258 : : g_test_skip ("openpty not found");
259 : : #endif
260 : : }
261 : :
262 : : static void
263 : 1 : test_pollable_unix_file (void)
264 : : {
265 : : int fd;
266 : :
267 : 1 : g_test_summary ("Test that regular files are not considered pollable");
268 : :
269 : 1 : fd = g_open ("/etc/hosts", O_RDONLY, 0);
270 : 1 : if (fd == -1)
271 : : {
272 : 0 : g_test_skip ("Unable to open /etc/hosts");
273 : 0 : return;
274 : : }
275 : :
276 : 1 : g_assert_not_pollable (fd);
277 : :
278 : 1 : close (fd);
279 : : }
280 : :
281 : : static void
282 : 1 : test_pollable_unix_nulldev (void)
283 : : {
284 : 1 : g_test_summary ("Test that /dev/null is not considered pollable, but only if "
285 : : "on a system where we are able to tell it apart from devices "
286 : : "that actually implement poll");
287 : :
288 : : #if defined (HAVE_EPOLL_CREATE1) || defined (HAVE_KQUEUE)
289 : 1 : int fd = g_open ("/dev/null", O_RDWR, 0);
290 : 1 : g_assert_cmpint (fd, !=, -1);
291 : :
292 : : #if defined(__FreeBSD__)
293 : : int freebsd_version = getosreldate ();
294 : : g_assert_cmpint (freebsd_version, !=, -1);
295 : : /* /dev/null is actually pollable on FreeBSD 14.4+ with both poll(2) and kevent(2) */
296 : : if (freebsd_version >= 1404000)
297 : : g_assert_pollable (fd);
298 : : else
299 : : g_assert_not_pollable (fd);
300 : : #else
301 : 1 : g_assert_not_pollable (fd);
302 : : #endif
303 : :
304 : 1 : close (fd);
305 : : #else
306 : : g_test_skip ("Cannot detect /dev/null as non-pollable on this system");
307 : : #endif
308 : 1 : }
309 : :
310 : : static void
311 : 1 : test_pollable_converter (void)
312 : : {
313 : : GConverter *converter;
314 : 1 : GError *error = NULL;
315 : : GInputStream *ibase;
316 : : int pipefds[2], status;
317 : 1 : GPollableInputStream *in = NULL;
318 : 1 : GOutputStream *out = NULL;
319 : :
320 : 1 : status = pipe (pipefds);
321 : 1 : g_assert_cmpint (status, ==, 0);
322 : :
323 : 1 : ibase = G_INPUT_STREAM (g_unix_input_stream_new (pipefds[0], TRUE));
324 : 1 : converter = G_CONVERTER (g_charset_converter_new ("UTF-8", "UTF-8", &error));
325 : 1 : g_assert_no_error (error);
326 : :
327 : 1 : in = G_POLLABLE_INPUT_STREAM (g_converter_input_stream_new (ibase, converter));
328 : 1 : g_object_unref (converter);
329 : 1 : g_object_unref (ibase);
330 : :
331 : 1 : out = g_unix_output_stream_new (pipefds[1], TRUE);
332 : :
333 : 1 : test_streams (in, out);
334 : :
335 : 1 : g_object_unref (in);
336 : 1 : g_object_unref (out);
337 : 1 : }
338 : :
339 : : #endif
340 : :
341 : : static void
342 : 2 : client_connected (GObject *source,
343 : : GAsyncResult *result,
344 : : gpointer user_data)
345 : : {
346 : 2 : GSocketClient *client = G_SOCKET_CLIENT (source);
347 : 2 : GSocketConnection **conn = user_data;
348 : 2 : GError *error = NULL;
349 : :
350 : 2 : *conn = g_socket_client_connect_finish (client, result, &error);
351 : 2 : g_assert_no_error (error);
352 : 2 : }
353 : :
354 : : static void
355 : 2 : server_connected (GObject *source,
356 : : GAsyncResult *result,
357 : : gpointer user_data)
358 : : {
359 : 2 : GSocketListener *listener = G_SOCKET_LISTENER (source);
360 : 2 : GSocketConnection **conn = user_data;
361 : 2 : GError *error = NULL;
362 : :
363 : 2 : *conn = g_socket_listener_accept_finish (listener, result, NULL, &error);
364 : 2 : g_assert_no_error (error);
365 : 2 : }
366 : :
367 : : static void
368 : 2 : test_pollable_socket (void)
369 : : {
370 : : GInetAddress *iaddr;
371 : : GSocketAddress *saddr, *effective_address;
372 : : GSocketListener *listener;
373 : : GSocketClient *client;
374 : 2 : GError *error = NULL;
375 : 2 : GSocketConnection *client_conn = NULL, *server_conn = NULL;
376 : 2 : GPollableInputStream *in = NULL;
377 : 2 : GOutputStream *out = NULL;
378 : :
379 : 2 : iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
380 : 2 : saddr = g_inet_socket_address_new (iaddr, 0);
381 : 2 : g_object_unref (iaddr);
382 : :
383 : 2 : listener = g_socket_listener_new ();
384 : 2 : g_socket_listener_add_address (listener, saddr,
385 : : G_SOCKET_TYPE_STREAM,
386 : : G_SOCKET_PROTOCOL_TCP,
387 : : NULL,
388 : : &effective_address,
389 : : &error);
390 : 2 : g_assert_no_error (error);
391 : 2 : g_object_unref (saddr);
392 : :
393 : 2 : client = g_socket_client_new ();
394 : :
395 : 3 : g_socket_client_connect_async (client,
396 : 2 : G_SOCKET_CONNECTABLE (effective_address),
397 : : NULL, client_connected, &client_conn);
398 : 2 : g_socket_listener_accept_async (listener, NULL,
399 : : server_connected, &server_conn);
400 : :
401 : 8 : while (!client_conn || !server_conn)
402 : 6 : g_main_context_iteration (NULL, TRUE);
403 : :
404 : 2 : in = G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (G_IO_STREAM (client_conn)));
405 : 2 : out = g_io_stream_get_output_stream (G_IO_STREAM (server_conn));
406 : :
407 : 2 : test_streams (in, out);
408 : :
409 : 2 : g_object_unref (client_conn);
410 : 2 : g_object_unref (server_conn);
411 : 2 : g_object_unref (client);
412 : 2 : g_object_unref (listener);
413 : 2 : g_object_unref (effective_address);
414 : 2 : }
415 : :
416 : : int
417 : 2 : main (int argc,
418 : : char *argv[])
419 : : {
420 : 2 : g_test_init (&argc, &argv, NULL);
421 : :
422 : : #ifdef G_OS_UNIX
423 : 1 : g_test_add_func ("/pollable/unix/pipe", test_pollable_unix_pipe);
424 : 1 : g_test_add_func ("/pollable/unix/pty", test_pollable_unix_pty);
425 : 1 : g_test_add_func ("/pollable/unix/file", test_pollable_unix_file);
426 : 1 : g_test_add_func ("/pollable/unix/nulldev", test_pollable_unix_nulldev);
427 : 1 : g_test_add_func ("/pollable/converter", test_pollable_converter);
428 : : #endif
429 : 2 : g_test_add_func ("/pollable/socket", test_pollable_socket);
430 : :
431 : 2 : return g_test_run();
432 : : }
|