• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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