• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.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
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: David Zeuthen <davidz@redhat.com>
19  */
20 
21 #include <gio/gio.h>
22 #include <unistd.h>
23 #include <string.h>
24 
25 #include <stdlib.h>
26 
27 #ifdef G_OS_UNIX
28 #include <gio/gunixinputstream.h>
29 #include <gio/gunixoutputstream.h>
30 #include <gio/gunixconnection.h>
31 #endif
32 
33 #include "gdbus-tests.h"
34 
35 static GMainLoop *loop = NULL;
36 
37 /* ---------------------------------------------------------------------------------------------------- */
38 #ifdef G_OS_UNIX
39 
40 #include "test-pipe-unix.h"
41 #include "test-io-stream.h"
42 
43 /* ---------------------------------------------------------------------------------------------------- */
44 
45 static const GDBusArgInfo pokee_method_poke_out_arg0 = {
46   -1,   /* ref_count */
47   "result",
48   "s",
49   NULL  /* annotations */
50 };
51 
52 static const GDBusArgInfo *pokee_method_poke_out_args[2] = {
53   &pokee_method_poke_out_arg0,
54   NULL,
55 };
56 
57 static const GDBusArgInfo pokee_method_poke_in_arg0 = {
58   -1,   /* ref_count */
59   "value",
60   "s",
61   NULL  /* annotations */
62 };
63 
64 static const GDBusArgInfo *pokee_method_poke_in_args[2] = {
65   &pokee_method_poke_in_arg0,
66   NULL,
67 };
68 
69 static const GDBusMethodInfo pokee_method_poke = {
70   -1,   /* ref_count */
71   "Poke",
72   (GDBusArgInfo**) pokee_method_poke_in_args,
73   (GDBusArgInfo**) pokee_method_poke_out_args,
74   NULL  /* annotations */
75 };
76 
77 static const GDBusMethodInfo *pokee_methods[2] = {
78   &pokee_method_poke,
79   NULL
80 };
81 
82 static const GDBusInterfaceInfo pokee_object_info = {
83   -1,  /* ref_count */
84   "org.gtk.GDBus.Pokee",
85   (GDBusMethodInfo**) pokee_methods,
86   NULL, /* signals */
87   NULL, /* properties */
88   NULL  /* annotations */
89 };
90 
91 static void
pokee_method_call(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation,gpointer user_data)92 pokee_method_call (GDBusConnection       *connection,
93                    const gchar           *sender,
94                    const gchar           *object_path,
95                    const gchar           *interface_name,
96                    const gchar           *method_name,
97                    GVariant              *parameters,
98                    GDBusMethodInvocation *invocation,
99                    gpointer               user_data)
100 {
101   const gchar *str;
102   gchar *ret;
103 
104   g_assert_cmpstr (method_name, ==, "Poke");
105 
106   g_variant_get (parameters, "(&s)", &str);
107   ret = g_strdup_printf ("You poked me with: '%s'", str);
108   g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ret));
109   g_free (ret);
110 }
111 
112 static const GDBusInterfaceVTable pokee_vtable = {
113   pokee_method_call,
114   NULL, /* get_property */
115   NULL  /* set_property */
116 };
117 
118 /* Processes:
119  *
120  * parent
121  * \- first child (via fork()) is the pokee
122  * \- second child (via g_test_trap_fork()) is the poker
123  *
124  * The second child only exists to avoid sharing a main context between several
125  * second-children if we run a test resembling this one multiple times.
126  * See https://bugzilla.gnome.org/show_bug.cgi?id=658999 for why that's bad.
127  */
128 static void
test_non_socket(void)129 test_non_socket (void)
130 {
131   GIOStream *streams[2];
132   GDBusConnection *connection;
133   GError *error;
134   gchar *guid;
135   pid_t first_child;
136   GVariant *ret;
137   const gchar *str;
138   gboolean ok;
139 
140   error = NULL;
141 
142   ok = test_bidi_pipe (&streams[0], &streams[1], &error);
143   g_assert_no_error (error);
144   g_assert (ok);
145   g_assert (G_IS_IO_STREAM (streams[0]));
146   g_assert (G_IS_INPUT_STREAM (g_io_stream_get_input_stream (streams[0])));
147   g_assert (G_IS_OUTPUT_STREAM (g_io_stream_get_output_stream (streams[0])));
148   g_assert (G_IS_IO_STREAM (streams[1]));
149   g_assert (G_IS_INPUT_STREAM (g_io_stream_get_input_stream (streams[1])));
150   g_assert (G_IS_OUTPUT_STREAM (g_io_stream_get_output_stream (streams[1])));
151 
152   switch ((first_child = fork ()))
153     {
154     case -1:
155       g_assert_not_reached ();
156       break;
157 
158     case 0:
159       /* first child */
160 
161       /* we shouldn't do this in the parent, because we shouldn't use a
162        * GMainContext both before and after fork
163        */
164       loop = g_main_loop_new (NULL, FALSE);
165 
166       ok = g_io_stream_close (streams[1], NULL, &error);
167       g_assert_no_error (error);
168       g_assert (ok);
169       g_object_unref (streams[1]);
170 
171       guid = g_dbus_generate_guid ();
172       error = NULL;
173       /* We need to delay message processing to avoid the race
174        * described in
175        *
176        *  https://bugzilla.gnome.org/show_bug.cgi?id=627188
177        *
178        * This is because (early) dispatching is done on the IO thread
179        * (method_call() isn't called until we're in the right thread
180        * though) so in rare cases the parent sends the message before
181        * we (the first child) register the object
182        */
183       connection = g_dbus_connection_new_sync (streams[0],
184                                                guid,
185                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER |
186                                                G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
187                                                NULL, /* GDBusAuthObserver */
188                                                NULL,
189                                                &error);
190       g_free (guid);
191       g_assert_no_error (error);
192       g_object_unref (streams[0]);
193 
194       /* make sure we exit along with the parent */
195       g_dbus_connection_set_exit_on_close (connection, TRUE);
196 
197       error = NULL;
198       g_dbus_connection_register_object (connection,
199                                          "/pokee",
200                                          (GDBusInterfaceInfo *) &pokee_object_info,
201                                          &pokee_vtable,
202                                          NULL, /* user_data */
203                                          NULL, /* user_data_free_func */
204                                          &error);
205       g_assert_no_error (error);
206 
207       /* and now start message processing */
208       g_dbus_connection_start_message_processing (connection);
209 
210       g_main_loop_run (loop);
211 
212       g_assert_not_reached ();
213       break;
214 
215     default:
216       /* parent continues below */
217       break;
218     }
219 
220   /* This is #ifdef G_OS_UNIX anyway, so just use g_test_trap_fork() */
221   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
222   if (!g_test_trap_fork (0, 0))
223     {
224       /* parent */
225       g_object_unref (streams[0]);
226       g_object_unref (streams[1]);
227 
228       g_test_trap_assert_passed ();
229       g_assert_cmpint (kill (first_child, SIGTERM), ==, 0);
230       return;
231     }
232   G_GNUC_END_IGNORE_DEPRECATIONS;
233 
234   /* second child */
235 
236   /* we shouldn't do this in the parent, because we shouldn't use a
237    * GMainContext both before and after fork
238    */
239   loop = g_main_loop_new (NULL, FALSE);
240 
241   ok = g_io_stream_close (streams[0], NULL, &error);
242   g_assert_no_error (error);
243   g_assert (ok);
244   g_object_unref (streams[0]);
245 
246   connection = g_dbus_connection_new_sync (streams[1],
247                                            NULL, /* guid */
248                                            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
249                                            NULL, /* GDBusAuthObserver */
250                                            NULL,
251                                            &error);
252   g_assert_no_error (error);
253   g_object_unref (streams[1]);
254 
255   /* poke the first child */
256   error = NULL;
257   ret = g_dbus_connection_call_sync (connection,
258                                      NULL, /* name */
259                                      "/pokee",
260                                      "org.gtk.GDBus.Pokee",
261                                      "Poke",
262                                      g_variant_new ("(s)", "I am the POKER!"),
263                                      G_VARIANT_TYPE ("(s)"), /* return type */
264                                      G_DBUS_CALL_FLAGS_NONE,
265                                      -1,
266                                      NULL, /* cancellable */
267                                      &error);
268   g_assert_no_error (error);
269   g_variant_get (ret, "(&s)", &str);
270   g_assert_cmpstr (str, ==, "You poked me with: 'I am the POKER!'");
271   g_variant_unref (ret);
272 
273   g_object_unref (connection);
274   g_main_loop_unref (loop);
275   exit (0);
276 }
277 
278 #else /* G_OS_UNIX */
279 
280 static void
test_non_socket(void)281 test_non_socket (void)
282 {
283   /* TODO: test this with e.g. GWin32InputStream/GWin32OutputStream */
284 }
285 #endif
286 
287 /* ---------------------------------------------------------------------------------------------------- */
288 
289 int
main(int argc,char * argv[])290 main (int   argc,
291       char *argv[])
292 {
293   gint ret;
294 
295   g_test_init (&argc, &argv, NULL);
296 
297   g_test_add_func ("/gdbus/non-socket", test_non_socket);
298 
299   ret = g_test_run();
300 
301   return ret;
302 }
303