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