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