4016b5ed8664d072d338555e83bab1b3c6959517
[platform/upstream/glib.git] / gio / tests / gdbus-peer.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 /* for open(2) */
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 #include <gio/gunixsocketaddress.h>
33 #include <gio/gunixfdlist.h>
34
35 #include "gdbus-tests.h"
36
37
38 #ifdef G_OS_UNIX
39 static gboolean is_unix = TRUE;
40 #else
41 static gboolean is_unix = FALSE;
42 #endif
43
44 static gchar *test_guid = NULL;
45 static GMainLoop *service_loop = NULL;
46 static GDBusServer *server = NULL;
47 static GMainLoop *loop = NULL;
48
49 /* ---------------------------------------------------------------------------------------------------- */
50 /* Test that peer-to-peer connections work */
51 /* ---------------------------------------------------------------------------------------------------- */
52
53
54 typedef struct
55 {
56   gboolean accept_connection;
57   gint num_connection_attempts;
58   GPtrArray *current_connections;
59   guint num_method_calls;
60   gboolean signal_received;
61 } PeerData;
62
63 static const gchar *test_interface_introspection_xml =
64   "<node>"
65   "  <interface name='org.gtk.GDBus.PeerTestInterface'>"
66   "    <method name='HelloPeer'>"
67   "      <arg type='s' name='greeting' direction='in'/>"
68   "      <arg type='s' name='response' direction='out'/>"
69   "    </method>"
70   "    <method name='EmitSignal'/>"
71   "    <method name='OpenFile'>"
72   "      <arg type='s' name='path' direction='in'/>"
73   "    </method>"
74   "    <signal name='PeerSignal'>"
75   "      <arg type='s' name='a_string'/>"
76   "    </signal>"
77   "    <property type='s' name='PeerProperty' access='read'/>"
78   "  </interface>"
79   "</node>";
80 static const GDBusInterfaceInfo *test_interface_introspection_data = NULL;
81
82 static void
83 test_interface_method_call (GDBusConnection       *connection,
84                             const gchar           *sender,
85                             const gchar           *object_path,
86                             const gchar           *interface_name,
87                             const gchar           *method_name,
88                             GVariant              *parameters,
89                             GDBusMethodInvocation *invocation,
90                             gpointer               user_data)
91 {
92   PeerData *data = user_data;
93
94   data->num_method_calls++;
95
96   g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
97   g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
98
99   if (g_strcmp0 (method_name, "HelloPeer") == 0)
100     {
101       const gchar *greeting;
102       gchar *response;
103
104       g_variant_get (parameters, "(&s)", &greeting);
105
106       response = g_strdup_printf ("You greeted me with '%s'.",
107                                   greeting);
108       g_dbus_method_invocation_return_value (invocation,
109                                              g_variant_new ("(s)", response));
110       g_free (response);
111     }
112   else if (g_strcmp0 (method_name, "EmitSignal") == 0)
113     {
114       GError *error;
115
116       error = NULL;
117       g_dbus_connection_emit_signal (connection,
118                                      NULL,
119                                      "/org/gtk/GDBus/PeerTestObject",
120                                      "org.gtk.GDBus.PeerTestInterface",
121                                      "PeerSignal",
122                                      NULL,
123                                      &error);
124       g_assert_no_error (error);
125       g_dbus_method_invocation_return_value (invocation, NULL);
126     }
127   else if (g_strcmp0 (method_name, "OpenFile") == 0)
128     {
129 #ifdef G_OS_UNIX
130       const gchar *path;
131       GDBusMessage *reply;
132       GError *error;
133       gint fd;
134       GUnixFDList *fd_list;
135
136       g_variant_get (parameters, "(&s)", &path);
137
138       fd_list = g_unix_fd_list_new ();
139
140       error = NULL;
141
142       fd = open (path, O_RDONLY);
143       g_unix_fd_list_append (fd_list, fd, &error);
144       g_assert_no_error (error);
145       close (fd);
146
147       reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation));
148       g_dbus_message_set_unix_fd_list (reply, fd_list);
149       g_object_unref (invocation);
150
151       error = NULL;
152       g_dbus_connection_send_message (connection,
153                                       reply,
154                                       NULL, /* out_serial */
155                                       &error);
156       g_assert_no_error (error);
157       g_object_unref (reply);
158 #else
159       g_dbus_method_invocation_return_dbus_error (invocation,
160                                                   "org.gtk.GDBus.NotOnUnix",
161                                                   "Your OS does not support file descriptor passing");
162 #endif
163     }
164   else
165     {
166       g_assert_not_reached ();
167     }
168 }
169
170 static GVariant *
171 test_interface_get_property (GDBusConnection  *connection,
172                              const gchar      *sender,
173                              const gchar      *object_path,
174                              const gchar      *interface_name,
175                              const gchar      *property_name,
176                              GError          **error,
177                              gpointer          user_data)
178 {
179   g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject");
180   g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface");
181   g_assert_cmpstr (property_name, ==, "PeerProperty");
182
183   return g_variant_new_string ("ThePropertyValue");
184 }
185
186
187 static const GDBusInterfaceVTable test_interface_vtable =
188 {
189   test_interface_method_call,
190   test_interface_get_property,
191   NULL  /* set_property */
192 };
193
194 static void
195 on_proxy_signal_received (GDBusProxy *proxy,
196                           gchar      *sender_name,
197                           gchar      *signal_name,
198                           GVariant   *parameters,
199                           gpointer    user_data)
200 {
201   PeerData *data = user_data;
202
203   data->signal_received = TRUE;
204
205   g_assert (sender_name == NULL);
206   g_assert_cmpstr (signal_name, ==, "PeerSignal");
207   g_main_loop_quit (loop);
208 }
209
210 /* ---------------------------------------------------------------------------------------------------- */
211
212 static gboolean
213 on_authorize_authenticated_peer (GDBusAuthObserver *observer,
214                                  GIOStream         *stream,
215                                  GCredentials      *credentials,
216                                  gpointer           user_data)
217 {
218   PeerData *data = user_data;
219   gboolean authorized;
220
221   data->num_connection_attempts++;
222
223   authorized = TRUE;
224   if (!data->accept_connection)
225     {
226       authorized = FALSE;
227       g_main_loop_quit (loop);
228     }
229
230   return authorized;
231 }
232
233 /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
234 static void
235 on_new_connection (GDBusServer *server,
236                    GDBusConnection *connection,
237                    gpointer user_data)
238 {
239   PeerData *data = user_data;
240   GError *error;
241   guint reg_id;
242
243   //g_print ("Client connected.\n"
244   //         "Negotiated capabilities: unix-fd-passing=%d\n",
245   //         g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
246
247   g_ptr_array_add (data->current_connections, g_object_ref (connection));
248
249   /* export object on the newly established connection */
250   error = NULL;
251   reg_id = g_dbus_connection_register_object (connection,
252                                               "/org/gtk/GDBus/PeerTestObject",
253                                               test_interface_introspection_data,
254                                               &test_interface_vtable,
255                                               data,
256                                               NULL, /* GDestroyNotify for data */
257                                               &error);
258   g_assert_no_error (error);
259   g_assert (reg_id > 0);
260
261   g_main_loop_quit (loop);
262 }
263
264 static gpointer
265 service_thread_func (gpointer user_data)
266 {
267   PeerData *data = user_data;
268   GMainContext *service_context;
269   GDBusAuthObserver *observer;
270   GError *error;
271
272   service_context = g_main_context_new ();
273   g_main_context_push_thread_default (service_context);
274
275   error = NULL;
276   observer = g_dbus_auth_observer_new ();
277   server = g_dbus_server_new_sync (is_unix ? "unix:tmpdir=/tmp/gdbus-test-" : "nonce-tcp:",
278                                    G_DBUS_SERVER_FLAGS_NONE,
279                                    test_guid,
280                                    observer,
281                                    NULL, /* cancellable */
282                                    &error);
283   g_assert_no_error (error);
284
285   g_signal_connect (server,
286                     "new-connection",
287                     G_CALLBACK (on_new_connection),
288                     data);
289   g_signal_connect (observer,
290                     "authorize-authenticated-peer",
291                     G_CALLBACK (on_authorize_authenticated_peer),
292                     data);
293   g_object_unref (observer);
294
295   g_dbus_server_start (server);
296
297   service_loop = g_main_loop_new (service_context, FALSE);
298   g_main_loop_run (service_loop);
299
300   g_main_context_pop_thread_default (service_context);
301
302   g_main_loop_unref (service_loop);
303   g_main_context_unref (service_context);
304
305   /* test code specifically unrefs the server - see below */
306   g_assert (server == NULL);
307
308   return NULL;
309 }
310
311 #if 0
312 static gboolean
313 on_incoming_connection (GSocketService     *service,
314                         GSocketConnection  *socket_connection,
315                         GObject            *source_object,
316                         gpointer           user_data)
317 {
318   PeerData *data = user_data;
319
320   if (data->accept_connection)
321     {
322       GError *error;
323       guint reg_id;
324       GDBusConnection *connection;
325
326       error = NULL;
327       connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection),
328                                                test_guid,
329                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
330                                                NULL, /* cancellable */
331                                                &error);
332       g_assert_no_error (error);
333
334       g_ptr_array_add (data->current_connections, connection);
335
336       /* export object on the newly established connection */
337       error = NULL;
338       reg_id = g_dbus_connection_register_object (connection,
339                                                   "/org/gtk/GDBus/PeerTestObject",
340                                                   &test_interface_introspection_data,
341                                                   &test_interface_vtable,
342                                                   data,
343                                                   NULL, /* GDestroyNotify for data */
344                                                   &error);
345       g_assert_no_error (error);
346       g_assert (reg_id > 0);
347
348     }
349   else
350     {
351       /* don't do anything */
352     }
353
354   data->num_connection_attempts++;
355
356   g_main_loop_quit (loop);
357
358   /* stops other signal handlers from being invoked */
359   return TRUE;
360 }
361
362 static gpointer
363 service_thread_func (gpointer data)
364 {
365   GMainContext *service_context;
366   gchar *socket_path;
367   GSocketAddress *address;
368   GError *error;
369
370   service_context = g_main_context_new ();
371   g_main_context_push_thread_default (service_context);
372
373   socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d", getpid ());
374   address = g_unix_socket_address_new (socket_path);
375
376   service = g_socket_service_new ();
377   error = NULL;
378   g_socket_listener_add_address (G_SOCKET_LISTENER (service),
379                                  address,
380                                  G_SOCKET_TYPE_STREAM,
381                                  G_SOCKET_PROTOCOL_DEFAULT,
382                                  NULL, /* source_object */
383                                  NULL, /* effective_address */
384                                  &error);
385   g_assert_no_error (error);
386   g_signal_connect (service,
387                     "incoming",
388                     G_CALLBACK (on_incoming_connection),
389                     data);
390   g_socket_service_start (service);
391
392   service_loop = g_main_loop_new (service_context, FALSE);
393   g_main_loop_run (service_loop);
394
395   g_main_context_pop_thread_default (service_context);
396
397   g_main_loop_unref (service_loop);
398   g_main_context_unref (service_context);
399
400   g_object_unref (address);
401   g_free (socket_path);
402   return NULL;
403 }
404 #endif
405
406 /* ---------------------------------------------------------------------------------------------------- */
407
408 #if 0
409 static gboolean
410 check_connection (gpointer user_data)
411 {
412   PeerData *data = user_data;
413   guint n;
414
415   for (n = 0; n < data->current_connections->len; n++)
416     {
417       GDBusConnection *c;
418       GIOStream *stream;
419
420       c = G_DBUS_CONNECTION (data->current_connections->pdata[n]);
421       stream = g_dbus_connection_get_stream (c);
422
423       g_debug ("In check_connection for %d: connection %p, stream %p", n, c, stream);
424       g_debug ("closed = %d", g_io_stream_is_closed (stream));
425
426       GSocket *socket;
427       socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
428       g_debug ("socket_closed = %d", g_socket_is_closed (socket));
429       g_debug ("socket_condition_check = %d", g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP));
430
431       gchar buf[128];
432       GError *error;
433       gssize num_read;
434       error = NULL;
435       num_read = g_input_stream_read (g_io_stream_get_input_stream (stream),
436                                       buf,
437                                       128,
438                                       NULL,
439                                       &error);
440       if (num_read < 0)
441         {
442           g_debug ("error: %s", error->message);
443           g_error_free (error);
444         }
445       else
446         {
447           g_debug ("no error, read %d bytes", (gint) num_read);
448         }
449     }
450
451   return FALSE;
452 }
453
454 static gboolean
455 on_do_disconnect_in_idle (gpointer data)
456 {
457   GDBusConnection *c = G_DBUS_CONNECTION (data);
458   g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count);
459   g_dbus_connection_disconnect (c);
460   g_object_unref (c);
461   return FALSE;
462 }
463 #endif
464
465 static void
466 test_peer (void)
467 {
468   GDBusConnection *c;
469   GDBusConnection *c2;
470   GDBusProxy *proxy;
471   GError *error;
472   PeerData data;
473   GVariant *value;
474   GVariant *result;
475   const gchar *s;
476   GThread *service_thread;
477
478   memset (&data, '\0', sizeof (PeerData));
479   data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
480
481   /* first try to connect when there is no server */
482   error = NULL;
483   c = g_dbus_connection_new_for_address_sync (is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" :
484                                               /* NOTE: Even if something is listening on port 12345 the connection
485                                                * will fail because the nonce file doesn't exist */
486                                               "nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus",
487                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
488                                               NULL, /* GDBusAuthObserver */
489                                               NULL, /* cancellable */
490                                               &error);
491   _g_assert_error_domain (error, G_IO_ERROR);
492   g_assert (!g_dbus_error_is_remote_error (error));
493   g_clear_error (&error);
494   g_assert (c == NULL);
495
496   /* bring up a server - we run the server in a different thread to avoid deadlocks */
497   error = NULL;
498   service_thread = g_thread_create (service_thread_func,
499                                     &data,
500                                     TRUE,
501                                     &error);
502   while (service_loop == NULL)
503     g_thread_yield ();
504   g_assert (server != NULL);
505
506   /* bring up a connection and accept it */
507   data.accept_connection = TRUE;
508   error = NULL;
509   c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
510                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
511                                               NULL, /* GDBusAuthObserver */
512                                               NULL, /* cancellable */
513                                               &error);
514   g_assert_no_error (error);
515   g_assert (c != NULL);
516   while (data.current_connections->len < 1)
517     g_main_loop_run (loop);
518   g_assert_cmpint (data.current_connections->len, ==, 1);
519   g_assert_cmpint (data.num_connection_attempts, ==, 1);
520   g_assert (g_dbus_connection_get_unique_name (c) == NULL);
521   g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid);
522
523   /* check that we create a proxy, read properties, receive signals and invoke
524    * the HelloPeer() method. Since the server runs in another thread it's fine
525    * to use synchronous blocking API here.
526    */
527   error = NULL;
528   proxy = g_dbus_proxy_new_sync (c,
529                                  G_DBUS_PROXY_FLAGS_NONE,
530                                  NULL,
531                                  NULL, /* bus_name */
532                                  "/org/gtk/GDBus/PeerTestObject",
533                                  "org.gtk.GDBus.PeerTestInterface",
534                                  NULL, /* GCancellable */
535                                  &error);
536   g_assert_no_error (error);
537   g_assert (proxy != NULL);
538   error = NULL;
539   value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty");
540   g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue");
541
542   /* try invoking a method */
543   error = NULL;
544   result = g_dbus_proxy_call_sync (proxy,
545                                    "HelloPeer",
546                                    g_variant_new ("(s)", "Hey Peer!"),
547                                    G_DBUS_CALL_FLAGS_NONE,
548                                    -1,
549                                    NULL,  /* GCancellable */
550                                    &error);
551   g_assert_no_error (error);
552   g_variant_get (result, "(&s)", &s);
553   g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'.");
554   g_variant_unref (result);
555   g_assert_cmpint (data.num_method_calls, ==, 1);
556
557   /* make the other peer emit a signal - catch it */
558   g_signal_connect (proxy,
559                     "g-signal",
560                     G_CALLBACK (on_proxy_signal_received),
561                     &data);
562   g_assert (!data.signal_received);
563   g_dbus_proxy_call (proxy,
564                      "EmitSignal",
565                      NULL,  /* no arguments */
566                      G_DBUS_CALL_FLAGS_NONE,
567                      -1,
568                      NULL,  /* GCancellable */
569                      NULL,  /* GAsyncReadyCallback - we don't care about the result */
570                      NULL); /* user_data */
571   g_main_loop_run (loop);
572   g_assert (data.signal_received);
573   g_assert_cmpint (data.num_method_calls, ==, 2);
574
575   /* check for UNIX fd passing */
576 #ifdef G_OS_UNIX
577   {
578     GDBusMessage *method_call_message;
579     GDBusMessage *method_reply_message;
580     GUnixFDList *fd_list;
581     gint fd;
582     gchar buf[1024];
583     gssize len;
584     gchar *buf2;
585     gsize len2;
586
587     method_call_message = g_dbus_message_new_method_call (NULL, /* name */
588                                                           "/org/gtk/GDBus/PeerTestObject",
589                                                           "org.gtk.GDBus.PeerTestInterface",
590                                                           "OpenFile");
591     g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", "/etc/hosts"));
592     error = NULL;
593     method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
594                                                                            method_call_message,
595                                                                            -1,
596                                                                            NULL, /* out_serial */
597                                                                            NULL, /* cancellable */
598                                                                            &error);
599     g_assert_no_error (error);
600     g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
601     fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
602     g_assert (fd_list != NULL);
603     g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1);
604     error = NULL;
605     fd = g_unix_fd_list_get (fd_list, 0, &error);
606     g_assert_no_error (error);
607     g_object_unref (method_call_message);
608     g_object_unref (method_reply_message);
609
610     memset (buf, '\0', sizeof (buf));
611     len = read (fd, buf, sizeof (buf) - 1);
612     close (fd);
613
614     error = NULL;
615     g_file_get_contents ("/etc/hosts",
616                          &buf2,
617                          &len2,
618                          &error);
619     g_assert_no_error (error);
620     if (len2 > sizeof (buf))
621       buf2[sizeof (buf)] = '\0';
622     g_assert_cmpstr (buf, ==, buf2);
623     g_free (buf2);
624   }
625 #else
626   error = NULL;
627   result = g_dbus_proxy_call_sync (proxy,
628                                    "OpenFile",
629                                    g_variant_new ("(s)", "boo"),
630                                    G_DBUS_CALL_FLAGS_NONE,
631                                    -1,
632                                    NULL,  /* GCancellable */
633                                    &error);
634   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
635   g_assert (result == NULL);
636   g_error_free (error);
637 #endif /* G_OS_UNIX */
638
639
640   /* bring up a connection - don't accept it - this should fail
641    */
642   data.accept_connection = FALSE;
643   error = NULL;
644   c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
645                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
646                                                NULL, /* GDBusAuthObserver */
647                                                NULL, /* cancellable */
648                                                &error);
649   _g_assert_error_domain (error, G_IO_ERROR);
650   g_assert (c2 == NULL);
651
652 #if 0
653   /* TODO: THIS TEST DOESN'T WORK YET */
654
655   /* bring up a connection - accept it.. then disconnect from the client side - check
656    * that the server side gets the disconnect signal.
657    */
658   error = NULL;
659   data.accept_connection = TRUE;
660   c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server),
661                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
662                                                NULL, /* GDBusAuthObserver */
663                                                NULL, /* cancellable */
664                                                &error);
665   g_assert_no_error (error);
666   g_assert (c2 != NULL);
667   g_assert (!g_dbus_connection_get_is_disconnected (c2));
668   while (data.num_connection_attempts < 3)
669     g_main_loop_run (loop);
670   g_assert_cmpint (data.current_connections->len, ==, 2);
671   g_assert_cmpint (data.num_connection_attempts, ==, 3);
672   g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
673   g_idle_add (on_do_disconnect_in_idle, c2);
674   g_debug ("==================================================");
675   g_debug ("==================================================");
676   g_debug ("==================================================");
677   g_debug ("waiting for disconnect on connection %p, stream %p",
678            data.current_connections->pdata[1],
679            g_dbus_connection_get_stream (data.current_connections->pdata[1]));
680
681   g_timeout_add (2000, check_connection, &data);
682   //_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed");
683   g_main_loop_run (loop);
684   g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1])));
685   g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */
686 #endif
687
688   /* unref the server and stop listening for new connections
689    *
690    * This won't bring down the established connections - check that c is still connected
691    * by invoking a method
692    */
693   //g_socket_service_stop (service);
694   //g_object_unref (service);
695   g_dbus_server_stop (server);
696   g_object_unref (server);
697   server = NULL;
698
699   error = NULL;
700   result = g_dbus_proxy_call_sync (proxy,
701                                    "HelloPeer",
702                                    g_variant_new ("(s)", "Hey Again Peer!"),
703                                    G_DBUS_CALL_FLAGS_NONE,
704                                    -1,
705                                    NULL,  /* GCancellable */
706                                    &error);
707   g_assert_no_error (error);
708   g_variant_get (result, "(&s)", &s);
709   g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'.");
710   g_variant_unref (result);
711   g_assert_cmpint (data.num_method_calls, ==, 4);
712
713 #if 0
714   /* TODO: THIS TEST DOESN'T WORK YET */
715
716   /* now disconnect from the server side - check that the client side gets the signal */
717   g_assert_cmpint (data.current_connections->len, ==, 1);
718   g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c);
719   g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0]));
720   if (!g_dbus_connection_get_is_disconnected (c))
721     _g_assert_signal_received (c, "closed");
722   g_assert (g_dbus_connection_get_is_disconnected (c));
723 #endif
724
725   g_object_unref (c);
726   g_ptr_array_unref (data.current_connections);
727   g_object_unref (proxy);
728
729   g_main_loop_quit (service_loop);
730   g_thread_join (service_thread);
731 }
732
733 /* ---------------------------------------------------------------------------------------------------- */
734
735 typedef struct
736 {
737   GDBusServer *server;
738   GMainContext *context;
739   GMainLoop *loop;
740
741   GList *connections;
742 } DmpData;
743
744 static void
745 dmp_data_free (DmpData *data)
746 {
747   g_main_loop_unref (data->loop);
748   g_main_context_unref (data->context);
749   g_object_unref (data->server);
750   g_list_foreach (data->connections, (GFunc) g_object_unref, NULL);
751   g_list_free (data->connections);
752   g_free (data);
753 }
754
755 static void
756 dmp_on_method_call (GDBusConnection       *connection,
757                     const gchar           *sender,
758                     const gchar           *object_path,
759                     const gchar           *interface_name,
760                     const gchar           *method_name,
761                     GVariant              *parameters,
762                     GDBusMethodInvocation *invocation,
763                     gpointer               user_data)
764 {
765   //DmpData *data = user_data;
766   gint32 first;
767   gint32 second;
768   g_variant_get (parameters,
769                  "(ii)",
770                  &first,
771                  &second);
772   g_dbus_method_invocation_return_value (invocation,
773                                          g_variant_new ("(i)", first + second));
774 }
775
776 static const GDBusInterfaceVTable dmp_interface_vtable =
777 {
778   dmp_on_method_call,
779   NULL,  /* get_property */
780   NULL   /* set_property */
781 };
782
783
784 /* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */
785 static void
786 dmp_on_new_connection (GDBusServer     *server,
787                        GDBusConnection *connection,
788                        gpointer         user_data)
789 {
790   DmpData *data = user_data;
791   GDBusNodeInfo *node;
792   GError *error;
793
794   /* accept the connection */
795   data->connections = g_list_prepend (data->connections, g_object_ref (connection));
796
797   error = NULL;
798   node = g_dbus_node_info_new_for_xml ("<node>"
799                                        "  <interface name='org.gtk.GDBus.DmpInterface'>"
800                                        "    <method name='AddPair'>"
801                                        "      <arg type='i' name='first' direction='in'/>"
802                                        "      <arg type='i' name='second' direction='in'/>"
803                                        "      <arg type='i' name='sum' direction='out'/>"
804                                        "    </method>"
805                                        "  </interface>"
806                                        "</node>",
807                                        &error);
808   g_assert_no_error (error);
809
810   /* sleep 100ms before exporting an object - this is to test that
811    * G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING really works
812    * (GDBusServer uses this feature).
813    */
814   usleep (100 * 1000);
815
816   /* export an object */
817   error = NULL;
818   g_dbus_connection_register_object (connection,
819                                      "/dmp/test",
820                                      node->interfaces[0],
821                                      &dmp_interface_vtable,
822                                      data,
823                                      NULL,
824                                      &error);
825   g_dbus_node_info_unref (node);
826 }
827
828 static gpointer
829 dmp_thread_func (gpointer user_data)
830 {
831   DmpData *data = user_data;
832   GError *error;
833   gchar *guid;
834
835   data->context = g_main_context_new ();
836   g_main_context_push_thread_default (data->context);
837
838   error = NULL;
839   guid = g_dbus_generate_guid ();
840   data->server = g_dbus_server_new_sync ("nonce-tcp:",
841                                          G_DBUS_SERVER_FLAGS_NONE,
842                                          guid,
843                                          NULL, /* GDBusAuthObserver */
844                                          NULL, /* GCancellable */
845                                          &error);
846   g_assert_no_error (error);
847   g_signal_connect (data->server,
848                     "new-connection",
849                     G_CALLBACK (dmp_on_new_connection),
850                     data);
851
852   g_dbus_server_start (data->server);
853
854   data->loop = g_main_loop_new (data->context, FALSE);
855   g_main_loop_run (data->loop);
856
857   g_main_context_pop_thread_default (data->context);
858
859   g_free (guid);
860   return NULL;
861 }
862
863 static void
864 delayed_message_processing (void)
865 {
866   GError *error;
867   DmpData *data;
868   GThread *service_thread;
869   guint n;
870
871   data = g_new0 (DmpData, 1);
872
873   error = NULL;
874   service_thread = g_thread_create (dmp_thread_func,
875                                     data,
876                                     TRUE,
877                                     &error);
878   while (data->server == NULL || !g_dbus_server_is_active (data->server))
879     g_thread_yield ();
880
881   for (n = 0; n < 5; n++)
882     {
883       GDBusConnection *c;
884       GVariant *res;
885       gint32 val;
886
887       error = NULL;
888       c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (data->server),
889                                                   G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
890                                                   NULL, /* GDBusAuthObserver */
891                                                   NULL, /* GCancellable */
892                                                   &error);
893       g_assert_no_error (error);
894
895       error = NULL;
896       res = g_dbus_connection_call_sync (c,
897                                          NULL,    /* bus name */
898                                          "/dmp/test",
899                                          "org.gtk.GDBus.DmpInterface",
900                                          "AddPair",
901                                          g_variant_new ("(ii)", 2, n),
902                                          G_VARIANT_TYPE ("(i)"),
903                                          G_DBUS_CALL_FLAGS_NONE,
904                                          -1, /* timeout_msec */
905                                          NULL, /* GCancellable */
906                                          &error);
907       g_assert_no_error (error);
908       g_variant_get (res, "(i)", &val);
909       g_assert_cmpint (val, ==, 2 + n);
910       g_variant_unref (res);
911       g_object_unref (c);
912   }
913
914   g_main_loop_quit (data->loop);
915   g_thread_join (service_thread);
916   dmp_data_free (data);
917 }
918
919 /* ---------------------------------------------------------------------------------------------------- */
920
921 int
922 main (int   argc,
923       char *argv[])
924 {
925   gint ret;
926   GDBusNodeInfo *introspection_data = NULL;
927
928   g_type_init ();
929   g_thread_init (NULL);
930   g_test_init (&argc, &argv, NULL);
931
932   introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL);
933   g_assert (introspection_data != NULL);
934   test_interface_introspection_data = introspection_data->interfaces[0];
935
936   test_guid = g_dbus_generate_guid ();
937
938   /* all the tests rely on a shared main loop */
939   loop = g_main_loop_new (NULL, FALSE);
940
941   g_test_add_func ("/gdbus/peer-to-peer", test_peer);
942   g_test_add_func ("/gdbus/delayed-message-processing", delayed_message_processing);
943
944   ret = g_test_run();
945
946   g_main_loop_unref (loop);
947   g_free (test_guid);
948   g_dbus_node_info_unref (introspection_data);
949
950   return ret;
951 }