Bug 621945 – Filter outgoing messages in GDBusConnection
[platform/upstream/glib.git] / gio / tests / gdbus-connection.c
1 /* GLib testing framework examples and tests
2  *
3  * Copyright (C) 2008-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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include <gio/gio.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 #include "gdbus-tests.h"
28
29 /* all tests rely on a shared mainloop */
30 static GMainLoop *loop = NULL;
31
32 /* ---------------------------------------------------------------------------------------------------- */
33 /* Connection life-cycle testing */
34 /* ---------------------------------------------------------------------------------------------------- */
35
36 static void
37 test_connection_life_cycle (void)
38 {
39   GDBusConnection *c;
40   GDBusConnection *c2;
41   GError *error;
42
43   error = NULL;
44
45   /*
46    * Check for correct behavior when no bus is present
47    *
48    */
49   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
50   _g_assert_error_domain (error, G_IO_ERROR);
51   g_assert (!g_dbus_error_is_remote_error (error));
52   g_assert (c == NULL);
53   g_error_free (error);
54   error = NULL;
55
56   /*
57    *  Check for correct behavior when a bus is present
58    */
59   session_bus_up ();
60   /* case 1 */
61   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
62   g_assert_no_error (error);
63   g_assert (c != NULL);
64   g_assert (!g_dbus_connection_is_closed (c));
65
66   /*
67    * Check that singleton handling work
68    */
69   c2 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
70   g_assert_no_error (error);
71   g_assert (c2 != NULL);
72   g_assert (c == c2);
73   g_object_unref (c2);
74
75   /*
76    * Check that private connections work
77    */
78   c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
79   g_assert_no_error (error);
80   g_assert (c2 != NULL);
81   g_assert (c != c2);
82   g_object_unref (c2);
83
84   c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
85   g_assert_no_error (error);
86   g_assert (c2 != NULL);
87   g_assert (!g_dbus_connection_is_closed (c2));
88   g_dbus_connection_close (c2);
89   _g_assert_signal_received (c2, "closed");
90   g_assert (g_dbus_connection_is_closed (c2));
91   g_object_unref (c2);
92
93   /*
94    *  Check for correct behavior when the bus goes away
95    *
96    */
97   g_assert (!g_dbus_connection_is_closed (c));
98   g_dbus_connection_set_exit_on_close (c, FALSE);
99   session_bus_down ();
100   if (!g_dbus_connection_is_closed (c))
101     _g_assert_signal_received (c, "closed");
102   g_assert (g_dbus_connection_is_closed (c));
103
104   _g_object_wait_for_single_ref (c);
105   g_object_unref (c);
106 }
107
108 /* ---------------------------------------------------------------------------------------------------- */
109 /* Test that sending and receiving messages work as expected */
110 /* ---------------------------------------------------------------------------------------------------- */
111
112 static void
113 msg_cb_expect_error_disconnected (GDBusConnection *connection,
114                                   GAsyncResult    *res,
115                                   gpointer         user_data)
116 {
117   GError *error;
118   GVariant *result;
119
120   error = NULL;
121   result = g_dbus_connection_call_finish (connection,
122                                           res,
123                                           &error);
124   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
125   g_assert (!g_dbus_error_is_remote_error (error));
126   g_error_free (error);
127   g_assert (result == NULL);
128
129   g_main_loop_quit (loop);
130 }
131
132 static void
133 msg_cb_expect_error_unknown_method (GDBusConnection *connection,
134                                     GAsyncResult    *res,
135                                     gpointer         user_data)
136 {
137   GError *error;
138   GVariant *result;
139
140   error = NULL;
141   result = g_dbus_connection_call_finish (connection,
142                                           res,
143                                           &error);
144   g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
145   g_assert (g_dbus_error_is_remote_error (error));
146   g_assert (result == NULL);
147
148   g_main_loop_quit (loop);
149 }
150
151 static void
152 msg_cb_expect_success (GDBusConnection *connection,
153                        GAsyncResult    *res,
154                        gpointer         user_data)
155 {
156   GError *error;
157   GVariant *result;
158
159   error = NULL;
160   result = g_dbus_connection_call_finish (connection,
161                                           res,
162                                           &error);
163   g_assert_no_error (error);
164   g_assert (result != NULL);
165   g_variant_unref (result);
166
167   g_main_loop_quit (loop);
168 }
169
170 static void
171 msg_cb_expect_error_cancelled (GDBusConnection *connection,
172                                GAsyncResult    *res,
173                                gpointer         user_data)
174 {
175   GError *error;
176   GVariant *result;
177
178   error = NULL;
179   result = g_dbus_connection_call_finish (connection,
180                                           res,
181                                           &error);
182   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
183   g_assert (!g_dbus_error_is_remote_error (error));
184   g_error_free (error);
185   g_assert (result == NULL);
186
187   g_main_loop_quit (loop);
188 }
189
190 static void
191 msg_cb_expect_error_cancelled_2 (GDBusConnection *connection,
192                                  GAsyncResult    *res,
193                                  gpointer         user_data)
194 {
195   GError *error;
196   GVariant *result;
197
198   error = NULL;
199   result = g_dbus_connection_call_finish (connection,
200                                           res,
201                                           &error);
202   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
203   g_assert (!g_dbus_error_is_remote_error (error));
204   g_error_free (error);
205   g_assert (result == NULL);
206
207   g_main_loop_quit (loop);
208 }
209
210 /* ---------------------------------------------------------------------------------------------------- */
211
212 static void
213 test_connection_send (void)
214 {
215   GDBusConnection *c;
216   GCancellable *ca;
217
218   session_bus_up ();
219
220   /* First, get an unopened connection */
221   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
222   g_assert (c != NULL);
223   g_assert (!g_dbus_connection_is_closed (c));
224
225   /*
226    * Check that we never actually send a message if the GCancellable
227    * is already cancelled - i.e.  we should get #G_IO_ERROR_CANCELLED
228    * when the actual connection is not up.
229    */
230   ca = g_cancellable_new ();
231   g_cancellable_cancel (ca);
232   g_dbus_connection_call (c,
233                           "org.freedesktop.DBus",  /* bus_name */
234                           "/org/freedesktop/DBus", /* object path */
235                           "org.freedesktop.DBus",  /* interface name */
236                           "GetId",                 /* method name */
237                           NULL, NULL,
238                           G_DBUS_CALL_FLAGS_NONE,
239                           -1,
240                           ca,
241                           (GAsyncReadyCallback) msg_cb_expect_error_cancelled,
242                           NULL);
243   g_main_loop_run (loop);
244   g_object_unref (ca);
245
246   /*
247    * Check that we get a reply to the GetId() method call.
248    */
249   g_dbus_connection_call (c,
250                           "org.freedesktop.DBus",  /* bus_name */
251                           "/org/freedesktop/DBus", /* object path */
252                           "org.freedesktop.DBus",  /* interface name */
253                           "GetId",                 /* method name */
254                           NULL, NULL,
255                           G_DBUS_CALL_FLAGS_NONE,
256                           -1,
257                           NULL,
258                           (GAsyncReadyCallback) msg_cb_expect_success,
259                           NULL);
260   g_main_loop_run (loop);
261
262   /*
263    * Check that we get an error reply to the NonExistantMethod() method call.
264    */
265   g_dbus_connection_call (c,
266                           "org.freedesktop.DBus",  /* bus_name */
267                           "/org/freedesktop/DBus", /* object path */
268                           "org.freedesktop.DBus",  /* interface name */
269                           "NonExistantMethod",     /* method name */
270                           NULL, NULL,
271                           G_DBUS_CALL_FLAGS_NONE,
272                           -1,
273                           NULL,
274                           (GAsyncReadyCallback) msg_cb_expect_error_unknown_method,
275                           NULL);
276   g_main_loop_run (loop);
277
278   /*
279    * Check that cancellation works when the message is already in flight.
280    */
281   ca = g_cancellable_new ();
282   g_dbus_connection_call (c,
283                           "org.freedesktop.DBus",  /* bus_name */
284                           "/org/freedesktop/DBus", /* object path */
285                           "org.freedesktop.DBus",  /* interface name */
286                           "GetId",                 /* method name */
287                           NULL, NULL,
288                           G_DBUS_CALL_FLAGS_NONE,
289                           -1,
290                           ca,
291                           (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2,
292                           NULL);
293   g_cancellable_cancel (ca);
294   g_main_loop_run (loop);
295   g_object_unref (ca);
296
297   /*
298    * Check that we get an error when sending to a connection that is disconnected.
299    */
300   g_dbus_connection_set_exit_on_close (c, FALSE);
301   session_bus_down ();
302   _g_assert_signal_received (c, "closed");
303   g_assert (g_dbus_connection_is_closed (c));
304
305   g_dbus_connection_call (c,
306                           "org.freedesktop.DBus",  /* bus_name */
307                           "/org/freedesktop/DBus", /* object path */
308                           "org.freedesktop.DBus",  /* interface name */
309                           "GetId",                 /* method name */
310                           NULL, NULL,
311                           G_DBUS_CALL_FLAGS_NONE,
312                           -1,
313                           NULL,
314                           (GAsyncReadyCallback) msg_cb_expect_error_disconnected,
315                           NULL);
316   g_main_loop_run (loop);
317
318   _g_object_wait_for_single_ref (c);
319   g_object_unref (c);
320 }
321
322 /* ---------------------------------------------------------------------------------------------------- */
323 /* Connection signal tests */
324 /* ---------------------------------------------------------------------------------------------------- */
325
326 static void
327 test_connection_signal_handler (GDBusConnection  *connection,
328                                 const gchar      *sender_name,
329                                 const gchar      *object_path,
330                                 const gchar      *interface_name,
331                                 const gchar      *signal_name,
332                                 GVariant         *parameters,
333                                 gpointer         user_data)
334 {
335   gint *counter = user_data;
336   *counter += 1;
337
338   /*g_debug ("in test_connection_signal_handler (sender=%s path=%s interface=%s member=%s)",
339            sender_name,
340            object_path,
341            interface_name,
342            signal_name);*/
343
344   g_main_loop_quit (loop);
345 }
346
347 static gboolean
348 test_connection_signal_quit_mainloop (gpointer user_data)
349 {
350   gboolean *quit_mainloop_fired = user_data;
351   *quit_mainloop_fired = TRUE;
352   g_main_loop_quit (loop);
353   return TRUE;
354 }
355
356 static void
357 test_connection_signals (void)
358 {
359   GDBusConnection *c1;
360   GDBusConnection *c2;
361   GDBusConnection *c3;
362   guint s1;
363   guint s1b;
364   guint s2;
365   guint s3;
366   gint count_s1;
367   gint count_s1b;
368   gint count_s2;
369   gint count_name_owner_changed;
370   GError *error;
371   gboolean ret;
372   GVariant *result;
373
374   error = NULL;
375
376   /*
377    * Bring up first separate connections
378    */
379   session_bus_up ();
380   /* if running with dbus-monitor, it claims the name :1.0 - so if we don't run with the monitor
381    * emulate this
382    */
383   if (g_getenv ("G_DBUS_MONITOR") == NULL)
384     {
385       c1 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
386       g_assert (c1 != NULL);
387       g_assert (!g_dbus_connection_is_closed (c1));
388       g_object_unref (c1);
389     }
390   c1 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
391   g_assert (c1 != NULL);
392   g_assert (!g_dbus_connection_is_closed (c1));
393   g_assert_cmpstr (g_dbus_connection_get_unique_name (c1), ==, ":1.1");
394
395   /*
396    * Install two signal handlers for the first connection
397    *
398    *  - Listen to the signal "Foo" from :1.2 (e.g. c2)
399    *  - Listen to the signal "Foo" from anyone (e.g. both c2 and c3)
400    *
401    * and then count how many times this signal handler was invoked.
402    */
403   s1 = g_dbus_connection_signal_subscribe (c1,
404                                            ":1.2",
405                                            "org.gtk.GDBus.ExampleInterface",
406                                            "Foo",
407                                            "/org/gtk/GDBus/ExampleInterface",
408                                            NULL,
409                                            test_connection_signal_handler,
410                                            &count_s1,
411                                            NULL);
412   s2 = g_dbus_connection_signal_subscribe (c1,
413                                            NULL, /* match any sender */
414                                            "org.gtk.GDBus.ExampleInterface",
415                                            "Foo",
416                                            "/org/gtk/GDBus/ExampleInterface",
417                                            NULL,
418                                            test_connection_signal_handler,
419                                            &count_s2,
420                                            NULL);
421   s3 = g_dbus_connection_signal_subscribe (c1,
422                                            "org.freedesktop.DBus",  /* sender */
423                                            "org.freedesktop.DBus",  /* interface */
424                                            "NameOwnerChanged",      /* member */
425                                            "/org/freedesktop/DBus", /* path */
426                                            NULL,
427                                            test_connection_signal_handler,
428                                            &count_name_owner_changed,
429                                            NULL);
430   /* Note that s1b is *just like* s1 - this is to catch a bug where N
431    * subscriptions of the same rule causes N calls to each of the N
432    * subscriptions instead of just 1 call to each of the N subscriptions.
433    */
434   s1b = g_dbus_connection_signal_subscribe (c1,
435                                            ":1.2",
436                                            "org.gtk.GDBus.ExampleInterface",
437                                            "Foo",
438                                            "/org/gtk/GDBus/ExampleInterface",
439                                            NULL,
440                                            test_connection_signal_handler,
441                                            &count_s1b,
442                                            NULL);
443   g_assert (s1 != 0);
444   g_assert (s1b != 0);
445   g_assert (s2 != 0);
446   g_assert (s3 != 0);
447
448   count_s1 = 0;
449   count_s1b = 0;
450   count_s2 = 0;
451   count_name_owner_changed = 0;
452
453   /*
454    * Bring up two other connections
455    */
456   c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
457   g_assert (c2 != NULL);
458   g_assert (!g_dbus_connection_is_closed (c2));
459   g_assert_cmpstr (g_dbus_connection_get_unique_name (c2), ==, ":1.2");
460   c3 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
461   g_assert (c3 != NULL);
462   g_assert (!g_dbus_connection_is_closed (c3));
463   g_assert_cmpstr (g_dbus_connection_get_unique_name (c3), ==, ":1.3");
464
465   /*
466    * Make c2 emit "Foo" - we should catch it twice
467    *
468    * Note that there is no way to be sure that the signal subscriptions
469    * on c1 are effective yet - for all we know, the AddMatch() messages
470    * could sit waiting in a buffer somewhere between this process and
471    * the message bus. And emitting signals on c2 (a completely other
472    * socket!) will not necessarily change this.
473    *
474    * To ensure this is not the case, do a synchronous call on c1.
475    */
476   result = g_dbus_connection_call_sync (c1,
477                                         "org.freedesktop.DBus",  /* bus name */
478                                         "/org/freedesktop/DBus", /* object path */
479                                         "org.freedesktop.DBus",  /* interface name */
480                                         "GetId",                 /* method name */
481                                         NULL,                    /* parameters */
482                                         NULL,                    /* return type */
483                                         G_DBUS_CALL_FLAGS_NONE,
484                                         -1,
485                                         NULL,
486                                         &error);
487   g_assert_no_error (error);
488   g_assert (result != NULL);
489   g_variant_unref (result);
490   /* now, emit the signal on c2 */
491   ret = g_dbus_connection_emit_signal (c2,
492                                        NULL, /* destination bus name */
493                                        "/org/gtk/GDBus/ExampleInterface",
494                                        "org.gtk.GDBus.ExampleInterface",
495                                        "Foo",
496                                        NULL,
497                                        &error);
498   g_assert_no_error (error);
499   g_assert (ret);
500   while (!(count_s1 >= 1 && count_s2 >= 1))
501     g_main_loop_run (loop);
502   g_assert_cmpint (count_s1, ==, 1);
503   g_assert_cmpint (count_s2, ==, 1);
504
505   /*
506    * Make c3 emit "Foo" - we should catch it only once
507    */
508   ret = g_dbus_connection_emit_signal (c3,
509                                        NULL, /* destination bus name */
510                                        "/org/gtk/GDBus/ExampleInterface",
511                                        "org.gtk.GDBus.ExampleInterface",
512                                        "Foo",
513                                        NULL,
514                                        &error);
515   g_assert_no_error (error);
516   g_assert (ret);
517   while (!(count_s1 == 1 && count_s2 == 2))
518     g_main_loop_run (loop);
519   g_assert_cmpint (count_s1, ==, 1);
520   g_assert_cmpint (count_s2, ==, 2);
521
522   /*
523    * Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling
524    * to avoid spinning forever
525    */
526   gboolean quit_mainloop_fired;
527   guint quit_mainloop_id;
528   quit_mainloop_fired = FALSE;
529   quit_mainloop_id = g_timeout_add (5000, test_connection_signal_quit_mainloop, &quit_mainloop_fired);
530   while (count_name_owner_changed < 2 && !quit_mainloop_fired)
531     g_main_loop_run (loop);
532   g_source_remove (quit_mainloop_id);
533   g_assert_cmpint (count_s1, ==, 1);
534   g_assert_cmpint (count_s2, ==, 2);
535   g_assert_cmpint (count_name_owner_changed, ==, 2);
536
537   g_dbus_connection_signal_unsubscribe (c1, s1);
538   g_dbus_connection_signal_unsubscribe (c1, s2);
539   g_dbus_connection_signal_unsubscribe (c1, s3);
540   g_dbus_connection_signal_unsubscribe (c1, s1b);
541
542   _g_object_wait_for_single_ref (c1);
543   _g_object_wait_for_single_ref (c2);
544   _g_object_wait_for_single_ref (c3);
545
546   g_object_unref (c1);
547   g_object_unref (c2);
548   g_object_unref (c3);
549
550   session_bus_down ();
551 }
552
553 /* ---------------------------------------------------------------------------------------------------- */
554
555 typedef struct
556 {
557   guint num_handled;
558   guint num_outgoing;
559   guint32 serial;
560 } FilterData;
561
562 static gboolean
563 filter_func (GDBusConnection *connection,
564              GDBusMessage    *message,
565              gboolean         incoming,
566              gpointer         user_data)
567 {
568   FilterData *data = user_data;
569   guint32 reply_serial;
570
571   if (incoming)
572     {
573       reply_serial = g_dbus_message_get_reply_serial (message);
574       if (reply_serial == data->serial)
575         data->num_handled += 1;
576     }
577   else
578     {
579       data->num_outgoing += 1;
580     }
581
582   return FALSE;
583 }
584
585 static void
586 test_connection_filter (void)
587 {
588   GDBusConnection *c;
589   FilterData data;
590   GDBusMessage *m;
591   GDBusMessage *r;
592   GError *error;
593   guint filter_id;
594
595   memset (&data, '\0', sizeof (FilterData));
596
597   session_bus_up ();
598
599   error = NULL;
600   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
601   g_assert_no_error (error);
602   g_assert (c != NULL);
603
604   filter_id = g_dbus_connection_add_filter (c,
605                                             filter_func,
606                                             &data,
607                                             NULL);
608
609   m = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
610                                       "/org/freedesktop/DBus", /* path */
611                                       "org.freedesktop.DBus", /* interface */
612                                       "GetNameOwner");
613   g_dbus_message_set_body (m, g_variant_new ("(s)", "org.freedesktop.DBus"));
614   error = NULL;
615   g_dbus_connection_send_message (c, m, &data.serial, &error);
616   g_assert_no_error (error);
617
618   while (data.num_handled == 0)
619     g_thread_yield ();
620
621   g_dbus_connection_send_message (c, m, &data.serial, &error);
622   g_assert_no_error (error);
623
624   while (data.num_handled == 1)
625     g_thread_yield ();
626
627   r = g_dbus_connection_send_message_with_reply_sync (c,
628                                                       m,
629                                                       -1,
630                                                       &data.serial,
631                                                       NULL, /* GCancellable */
632                                                       &error);
633   g_assert_no_error (error);
634   g_assert (r != NULL);
635   g_object_unref (r);
636   g_assert_cmpint (data.num_handled, ==, 3);
637
638   g_dbus_connection_remove_filter (c, filter_id);
639
640   r = g_dbus_connection_send_message_with_reply_sync (c,
641                                                       m,
642                                                       -1,
643                                                       &data.serial,
644                                                       NULL, /* GCancellable */
645                                                       &error);
646   g_assert_no_error (error);
647   g_assert (r != NULL);
648   g_object_unref (r);
649   g_assert_cmpint (data.num_handled, ==, 3);
650   g_assert_cmpint (data.num_outgoing, ==, 3);
651
652   _g_object_wait_for_single_ref (c);
653   g_object_unref (c);
654   g_object_unref (m);
655
656   session_bus_down ();
657 }
658
659 /* ---------------------------------------------------------------------------------------------------- */
660
661 int
662 main (int   argc,
663       char *argv[])
664 {
665   g_type_init ();
666   g_test_init (&argc, &argv, NULL);
667
668   /* all the tests rely on a shared main loop */
669   loop = g_main_loop_new (NULL, FALSE);
670
671   /* all the tests use a session bus with a well-known address that we can bring up and down
672    * using session_bus_up() and session_bus_down().
673    */
674   g_unsetenv ("DISPLAY");
675   g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
676
677   g_test_add_func ("/gdbus/connection-life-cycle", test_connection_life_cycle);
678   g_test_add_func ("/gdbus/connection-send", test_connection_send);
679   g_test_add_func ("/gdbus/connection-signals", test_connection_signals);
680   g_test_add_func ("/gdbus/connection-filter", test_connection_filter);
681   return g_test_run();
682 }