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