Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright 2014 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 Public
18 : : * License along with this library; if not, see
19 : : * <http://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include <gio/gio.h>
23 : :
24 : : static void
25 : 2 : active_notify_cb (GSocketService *service,
26 : : GParamSpec *pspec,
27 : : gpointer data)
28 : : {
29 : 2 : gboolean *success = (gboolean *)data;
30 : :
31 : 2 : if (g_socket_service_is_active (service))
32 : 1 : *success = TRUE;
33 : 2 : }
34 : :
35 : : static void
36 : 1 : connected_cb (GObject *client,
37 : : GAsyncResult *result,
38 : : gpointer user_data)
39 : : {
40 : 1 : GSocketService *service = G_SOCKET_SERVICE (user_data);
41 : : GSocketConnection *conn;
42 : 1 : GError *error = NULL;
43 : :
44 : 1 : g_assert_true (g_socket_service_is_active (service));
45 : :
46 : 1 : conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client), result, &error);
47 : 1 : g_assert_no_error (error);
48 : 1 : g_object_unref (conn);
49 : :
50 : 1 : g_socket_service_stop (service);
51 : 1 : g_assert_false (g_socket_service_is_active (service));
52 : 1 : }
53 : :
54 : : static void
55 : 1 : test_start_stop (void)
56 : : {
57 : 1 : gboolean success = FALSE;
58 : : GInetAddress *iaddr;
59 : : GSocketAddress *saddr, *listening_addr;
60 : : GSocketService *service;
61 : 1 : GError *error = NULL;
62 : : GSocketClient *client;
63 : :
64 : 1 : iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
65 : 1 : saddr = g_inet_socket_address_new (iaddr, 0);
66 : 1 : g_object_unref (iaddr);
67 : :
68 : : /* instantiate with g_object_new so we can pass active = false */
69 : 1 : service = g_object_new (G_TYPE_SOCKET_SERVICE, "active", FALSE, NULL);
70 : 1 : g_assert_false (g_socket_service_is_active (service));
71 : :
72 : 1 : g_signal_connect (service, "notify::active", G_CALLBACK (active_notify_cb), &success);
73 : :
74 : 1 : g_socket_listener_add_address (G_SOCKET_LISTENER (service),
75 : : saddr,
76 : : G_SOCKET_TYPE_STREAM,
77 : : G_SOCKET_PROTOCOL_TCP,
78 : : NULL,
79 : : &listening_addr,
80 : : &error);
81 : 1 : g_assert_no_error (error);
82 : 1 : g_object_unref (saddr);
83 : :
84 : 1 : client = g_socket_client_new ();
85 : 1 : g_socket_client_connect_async (client,
86 : 1 : G_SOCKET_CONNECTABLE (listening_addr),
87 : : NULL,
88 : : connected_cb, service);
89 : 1 : g_object_unref (client);
90 : 1 : g_object_unref (listening_addr);
91 : :
92 : 1 : g_socket_service_start (service);
93 : 1 : g_assert_true (g_socket_service_is_active (service));
94 : :
95 : : do
96 : 1 : g_main_context_iteration (NULL, TRUE);
97 : 1 : while (!success);
98 : :
99 : 1 : g_object_unref (service);
100 : 1 : }
101 : :
102 : : GMutex mutex_712570;
103 : : GCond cond_712570;
104 : : gboolean finalized; /* (atomic) */
105 : :
106 : : GType test_threaded_socket_service_get_type (void);
107 : : typedef GThreadedSocketService TestThreadedSocketService;
108 : : typedef GThreadedSocketServiceClass TestThreadedSocketServiceClass;
109 : :
110 : 3 : G_DEFINE_TYPE (TestThreadedSocketService, test_threaded_socket_service, G_TYPE_THREADED_SOCKET_SERVICE)
111 : :
112 : : static void
113 : 1 : test_threaded_socket_service_init (TestThreadedSocketService *service)
114 : : {
115 : 1 : }
116 : :
117 : : static void
118 : 1 : test_threaded_socket_service_finalize (GObject *object)
119 : : {
120 : 1 : G_OBJECT_CLASS (test_threaded_socket_service_parent_class)->finalize (object);
121 : :
122 : : /* Signal the main thread that finalization completed successfully
123 : : * rather than hanging.
124 : : */
125 : 1 : g_atomic_int_set (&finalized, TRUE);
126 : 1 : g_cond_signal (&cond_712570);
127 : 1 : g_mutex_unlock (&mutex_712570);
128 : 1 : }
129 : :
130 : : static void
131 : 1 : test_threaded_socket_service_class_init (TestThreadedSocketServiceClass *klass)
132 : : {
133 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
134 : :
135 : 1 : object_class->finalize = test_threaded_socket_service_finalize;
136 : 1 : }
137 : :
138 : : static gboolean
139 : 1 : connection_cb (GThreadedSocketService *service,
140 : : GSocketConnection *connection,
141 : : GObject *source_object,
142 : : gpointer user_data)
143 : : {
144 : 1 : GMainLoop *loop = user_data;
145 : :
146 : : /* Since the connection attempt has come through to be handled, stop the main
147 : : * thread waiting for it; this causes the #GSocketService to be stopped. */
148 : 1 : g_main_loop_quit (loop);
149 : :
150 : : /* Block until the main thread has dropped its ref to @service, so that we
151 : : * will drop the final ref from this thread.
152 : : */
153 : 1 : g_mutex_lock (&mutex_712570);
154 : :
155 : : /* The service should now have 1 ref owned by the current "run"
156 : : * signal emission, and another added by GThreadedSocketService for
157 : : * this thread. Both will be dropped after we return.
158 : : */
159 : 1 : g_assert_cmpint (G_OBJECT (service)->ref_count, ==, 2);
160 : :
161 : 1 : return FALSE;
162 : : }
163 : :
164 : : static void
165 : 1 : client_connected_cb (GObject *client,
166 : : GAsyncResult *result,
167 : : gpointer user_data)
168 : : {
169 : : GSocketConnection *conn;
170 : 1 : GError *error = NULL;
171 : :
172 : 1 : conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client), result, &error);
173 : 1 : g_assert_no_error (error);
174 : :
175 : 1 : g_object_unref (conn);
176 : 1 : }
177 : :
178 : : static void
179 : 1 : test_threaded_712570 (void)
180 : : {
181 : : GSocketService *service;
182 : : GSocketAddress *addr, *listening_addr;
183 : : GMainLoop *loop;
184 : : GSocketClient *client;
185 : 1 : GError *error = NULL;
186 : :
187 : 1 : g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=712570");
188 : :
189 : 1 : g_mutex_lock (&mutex_712570);
190 : :
191 : 1 : service = g_object_new (test_threaded_socket_service_get_type (), NULL);
192 : :
193 : 1 : addr = g_inet_socket_address_new_from_string ("127.0.0.1", 0);
194 : 1 : g_socket_listener_add_address (G_SOCKET_LISTENER (service),
195 : : addr,
196 : : G_SOCKET_TYPE_STREAM,
197 : : G_SOCKET_PROTOCOL_TCP,
198 : : NULL,
199 : : &listening_addr,
200 : : &error);
201 : 1 : g_assert_no_error (error);
202 : 1 : g_object_unref (addr);
203 : :
204 : 1 : loop = g_main_loop_new (NULL, FALSE);
205 : 1 : g_signal_connect (service, "run", G_CALLBACK (connection_cb), loop);
206 : :
207 : 1 : client = g_socket_client_new ();
208 : 1 : g_socket_client_connect_async (client,
209 : 1 : G_SOCKET_CONNECTABLE (listening_addr),
210 : : NULL,
211 : : client_connected_cb, loop);
212 : 1 : g_object_unref (client);
213 : 1 : g_object_unref (listening_addr);
214 : :
215 : 1 : g_main_loop_run (loop);
216 : 1 : g_main_loop_unref (loop);
217 : :
218 : : /* Stop the service and then wait for it to asynchronously cancel
219 : : * its outstanding accept() call (and drop the associated ref).
220 : : * At least one main context iteration is required in some circumstances
221 : : * to ensure that the cancellation actually happens.
222 : : */
223 : 1 : g_socket_service_stop (G_SOCKET_SERVICE (service));
224 : 1 : g_assert_false (g_socket_service_is_active (G_SOCKET_SERVICE (service)));
225 : :
226 : : do
227 : 2 : g_main_context_iteration (NULL, TRUE);
228 : 2 : while (G_OBJECT (service)->ref_count > 3);
229 : :
230 : : /* Wait some more iterations, as #GTask results are deferred to the next
231 : : * #GMainContext iteration, and propagation of a #GTask result takes an
232 : : * additional ref on the source object. */
233 : 1 : g_main_context_iteration (NULL, FALSE);
234 : :
235 : : /* Drop our ref, then unlock the mutex and wait for the service to be
236 : : * finalized. (Without the fix for 712570 it would hang forever here.)
237 : : */
238 : 1 : g_object_unref (service);
239 : :
240 : 2 : while (!g_atomic_int_get (&finalized))
241 : 1 : g_cond_wait (&cond_712570, &mutex_712570);
242 : 1 : g_mutex_unlock (&mutex_712570);
243 : 1 : }
244 : :
245 : : static void
246 : 4 : closed_read_write_async_cb (GSocketConnection *conn,
247 : : GAsyncResult *result,
248 : : gpointer user_data)
249 : : {
250 : 4 : GError *error = NULL;
251 : : gboolean res;
252 : :
253 : 4 : res = g_io_stream_close_finish (G_IO_STREAM (conn), result, &error);
254 : 4 : g_assert_no_error (error);
255 : 4 : g_assert_true (res);
256 : 4 : }
257 : :
258 : : typedef struct {
259 : : GSocketConnection *conn;
260 : : guint8 *data;
261 : : } WriteAsyncData;
262 : :
263 : : static void
264 : 1 : written_read_write_async_cb (GOutputStream *ostream,
265 : : GAsyncResult *result,
266 : : gpointer user_data)
267 : : {
268 : 1 : WriteAsyncData *data = user_data;
269 : 1 : GError *error = NULL;
270 : : gboolean res;
271 : : gsize bytes_written;
272 : : GSocketConnection *conn;
273 : :
274 : 1 : conn = data->conn;
275 : :
276 : 1 : g_free (data->data);
277 : 1 : g_free (data);
278 : :
279 : 1 : res = g_output_stream_write_all_finish (ostream, result, &bytes_written, &error);
280 : 1 : g_assert_no_error (error);
281 : 1 : g_assert_true (res);
282 : 1 : g_assert_cmpuint (bytes_written, ==, 20);
283 : :
284 : 1 : g_io_stream_close_async (G_IO_STREAM (conn),
285 : : G_PRIORITY_DEFAULT,
286 : : NULL,
287 : : (GAsyncReadyCallback) closed_read_write_async_cb,
288 : : NULL);
289 : 1 : g_object_unref (conn);
290 : 1 : }
291 : :
292 : : static void
293 : 1 : connected_read_write_async_cb (GObject *client,
294 : : GAsyncResult *result,
295 : : gpointer user_data)
296 : : {
297 : : GSocketConnection *conn;
298 : : GOutputStream *ostream;
299 : 1 : GError *error = NULL;
300 : : WriteAsyncData *data;
301 : : gsize i;
302 : 1 : GSocketConnection **sconn = user_data;
303 : :
304 : 1 : conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client), result, &error);
305 : 1 : g_assert_no_error (error);
306 : 1 : g_assert_nonnull (conn);
307 : :
308 : 1 : ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
309 : :
310 : 1 : data = g_new0 (WriteAsyncData, 1);
311 : 1 : data->conn = conn;
312 : 1 : data->data = g_new0 (guint8, 20);
313 : 21 : for (i = 0; i < 20; i++)
314 : 20 : data->data[i] = i;
315 : :
316 : 1 : g_output_stream_write_all_async (ostream,
317 : 1 : data->data,
318 : : 20,
319 : : G_PRIORITY_DEFAULT,
320 : : NULL,
321 : : (GAsyncReadyCallback) written_read_write_async_cb,
322 : : data /* stolen */);
323 : :
324 : 1 : *sconn = g_object_ref (conn);
325 : 1 : }
326 : :
327 : : typedef struct {
328 : : GSocketConnection *conn;
329 : : GOutputVector *vectors;
330 : : guint n_vectors;
331 : : guint8 *data;
332 : : } WritevAsyncData;
333 : :
334 : : static void
335 : 1 : writtenv_read_write_async_cb (GOutputStream *ostream,
336 : : GAsyncResult *result,
337 : : gpointer user_data)
338 : : {
339 : 1 : WritevAsyncData *data = user_data;
340 : 1 : GError *error = NULL;
341 : : gboolean res;
342 : : gsize bytes_written;
343 : : GSocketConnection *conn;
344 : :
345 : 1 : conn = data->conn;
346 : 1 : g_free (data->data);
347 : 1 : g_free (data->vectors);
348 : 1 : g_free (data);
349 : :
350 : 1 : res = g_output_stream_writev_all_finish (ostream, result, &bytes_written, &error);
351 : 1 : g_assert_no_error (error);
352 : 1 : g_assert_true (res);
353 : 1 : g_assert_cmpuint (bytes_written, ==, 20);
354 : :
355 : 1 : g_io_stream_close_async (G_IO_STREAM (conn),
356 : : G_PRIORITY_DEFAULT,
357 : : NULL,
358 : : (GAsyncReadyCallback) closed_read_write_async_cb,
359 : : NULL);
360 : 1 : g_object_unref (conn);
361 : 1 : }
362 : :
363 : : static void
364 : 1 : connected_read_writev_async_cb (GObject *client,
365 : : GAsyncResult *result,
366 : : gpointer user_data)
367 : : {
368 : : GSocketConnection *conn;
369 : : GOutputStream *ostream;
370 : 1 : GError *error = NULL;
371 : : WritevAsyncData *data;
372 : : gsize i;
373 : 1 : GSocketConnection **sconn = user_data;
374 : :
375 : 1 : conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client), result, &error);
376 : 1 : g_assert_no_error (error);
377 : 1 : g_assert_nonnull (conn);
378 : :
379 : 1 : ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
380 : :
381 : 1 : data = g_new0 (WritevAsyncData, 1);
382 : 1 : data->conn = conn;
383 : 1 : data->vectors = g_new0 (GOutputVector, 3);
384 : 1 : data->n_vectors = 3;
385 : 1 : data->data = g_new0 (guint8, 20);
386 : 21 : for (i = 0; i < 20; i++)
387 : 20 : data->data[i] = i;
388 : :
389 : 1 : data->vectors[0].buffer = data->data;
390 : 1 : data->vectors[0].size = 5;
391 : 1 : data->vectors[1].buffer = data->data + 5;
392 : 1 : data->vectors[1].size = 10;
393 : 1 : data->vectors[2].buffer = data->data + 15;
394 : 1 : data->vectors[2].size = 5;
395 : :
396 : 1 : g_output_stream_writev_all_async (ostream,
397 : : data->vectors,
398 : 1 : data->n_vectors,
399 : : G_PRIORITY_DEFAULT,
400 : : NULL,
401 : : (GAsyncReadyCallback) writtenv_read_write_async_cb,
402 : : data /* stolen */);
403 : :
404 : 1 : *sconn = g_object_ref (conn);
405 : 1 : }
406 : :
407 : : typedef struct {
408 : : GSocketConnection *conn;
409 : : guint8 *data;
410 : : } ReadAsyncData;
411 : :
412 : : static void
413 : 2 : read_read_write_async_cb (GInputStream *istream,
414 : : GAsyncResult *result,
415 : : gpointer user_data)
416 : : {
417 : 2 : ReadAsyncData *data = user_data;
418 : 2 : GError *error = NULL;
419 : : gboolean res;
420 : : gsize bytes_read;
421 : : GSocketConnection *conn;
422 : 2 : const guint8 expected_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
423 : :
424 : 2 : res = g_input_stream_read_all_finish (istream, result, &bytes_read, &error);
425 : 2 : g_assert_no_error (error);
426 : 2 : g_assert_true (res);
427 : :
428 : 2 : g_assert_cmpmem (expected_data, sizeof expected_data, data->data, bytes_read);
429 : :
430 : 2 : conn = data->conn;
431 : 2 : g_object_set_data (G_OBJECT (conn), "test-data-read", GINT_TO_POINTER (TRUE));
432 : :
433 : 2 : g_free (data->data);
434 : 2 : g_free (data);
435 : :
436 : 2 : g_io_stream_close_async (G_IO_STREAM (conn),
437 : : G_PRIORITY_DEFAULT,
438 : : NULL,
439 : : (GAsyncReadyCallback) closed_read_write_async_cb,
440 : : NULL);
441 : 2 : g_object_unref (conn);
442 : 2 : }
443 : :
444 : : static void
445 : 2 : incoming_read_write_async_cb (GSocketService *service,
446 : : GSocketConnection *conn,
447 : : GObject *source_object,
448 : : gpointer user_data)
449 : : {
450 : : ReadAsyncData *data;
451 : 2 : GSocketConnection **cconn = user_data;
452 : : GInputStream *istream;
453 : :
454 : 2 : istream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
455 : :
456 : 2 : data = g_new0 (ReadAsyncData, 1);
457 : 2 : data->conn = g_object_ref (conn);
458 : 2 : data->data = g_new0 (guint8, 20);
459 : :
460 : 2 : g_input_stream_read_all_async (istream,
461 : 2 : data->data,
462 : : 20,
463 : : G_PRIORITY_DEFAULT,
464 : : NULL,
465 : : (GAsyncReadyCallback) read_read_write_async_cb,
466 : : data /* stolen */);
467 : :
468 : 2 : *cconn = g_object_ref (conn);
469 : 2 : }
470 : :
471 : : static void
472 : 2 : test_read_write_async_internal (gboolean writev)
473 : : {
474 : : GInetAddress *iaddr;
475 : : GSocketAddress *saddr, *listening_addr;
476 : : GSocketService *service;
477 : 2 : GError *error = NULL;
478 : : GSocketClient *client;
479 : 2 : GSocketConnection *sconn = NULL, *cconn = NULL;
480 : :
481 : 2 : iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
482 : 2 : saddr = g_inet_socket_address_new (iaddr, 0);
483 : 2 : g_object_unref (iaddr);
484 : :
485 : 2 : service = g_socket_service_new ();
486 : :
487 : 2 : g_socket_listener_add_address (G_SOCKET_LISTENER (service),
488 : : saddr,
489 : : G_SOCKET_TYPE_STREAM,
490 : : G_SOCKET_PROTOCOL_TCP,
491 : : NULL,
492 : : &listening_addr,
493 : : &error);
494 : 2 : g_assert_no_error (error);
495 : 2 : g_object_unref (saddr);
496 : :
497 : 2 : g_signal_connect (service, "incoming", G_CALLBACK (incoming_read_write_async_cb), &sconn);
498 : :
499 : 2 : client = g_socket_client_new ();
500 : :
501 : 2 : if (writev)
502 : 1 : g_socket_client_connect_async (client,
503 : 1 : G_SOCKET_CONNECTABLE (listening_addr),
504 : : NULL,
505 : : connected_read_writev_async_cb,
506 : : &cconn);
507 : : else
508 : 1 : g_socket_client_connect_async (client,
509 : 1 : G_SOCKET_CONNECTABLE (listening_addr),
510 : : NULL,
511 : : connected_read_write_async_cb,
512 : : &cconn);
513 : :
514 : 2 : g_object_unref (client);
515 : 2 : g_object_unref (listening_addr);
516 : :
517 : 2 : g_socket_service_start (service);
518 : 2 : g_assert_true (g_socket_service_is_active (service));
519 : :
520 : : do
521 : : {
522 : 10 : g_main_context_iteration (NULL, TRUE);
523 : : }
524 : 12 : while (!sconn || !cconn ||
525 : 18 : !g_io_stream_is_closed (G_IO_STREAM (sconn)) ||
526 : 2 : !g_io_stream_is_closed (G_IO_STREAM (cconn)));
527 : :
528 : 2 : g_assert_true (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (sconn), "test-data-read")));
529 : :
530 : 2 : g_object_unref (sconn);
531 : 2 : g_object_unref (cconn);
532 : 2 : g_object_unref (service);
533 : 2 : }
534 : :
535 : : /* Test if connecting to a socket service and asynchronously writing data on
536 : : * one side followed by reading the same data on the other side of the
537 : : * connection works correctly
538 : : */
539 : : static void
540 : 1 : test_read_write_async (void)
541 : : {
542 : 1 : test_read_write_async_internal (FALSE);
543 : 1 : }
544 : :
545 : : /* Test if connecting to a socket service and asynchronously writing data on
546 : : * one side followed by reading the same data on the other side of the
547 : : * connection works correctly. This uses writev() instead of normal write().
548 : : */
549 : : static void
550 : 1 : test_read_writev_async (void)
551 : : {
552 : 1 : test_read_write_async_internal (TRUE);
553 : 1 : }
554 : :
555 : :
556 : : int
557 : 1 : main (int argc,
558 : : char *argv[])
559 : : {
560 : 1 : g_test_init (&argc, &argv, NULL);
561 : :
562 : 1 : g_test_add_func ("/socket-service/start-stop", test_start_stop);
563 : 1 : g_test_add_func ("/socket-service/threaded/712570", test_threaded_712570);
564 : 1 : g_test_add_func ("/socket-service/read_write_async", test_read_write_async);
565 : 1 : g_test_add_func ("/socket-service/read_writev_async", test_read_writev_async);
566 : :
567 : 1 : return g_test_run();
568 : : }
|