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 |
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 (value: parameters, format_string: "(&s)" , &str); |
107 | ret = g_strdup_printf (format: "You poked me with: '%s'" , str); |
108 | g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)" , ret)); |
109 | g_free (mem: 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 |
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 (left: &streams[0], right: &streams[1], error: &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 (stream: streams[1], NULL, error: &error); |
167 | g_assert_no_error (error); |
168 | g_assert (ok); |
169 | g_object_unref (object: 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 (stream: streams[0], |
184 | guid, |
185 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | |
186 | G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, |
187 | NULL, /* GDBusAuthObserver */ |
188 | NULL, |
189 | error: &error); |
190 | g_free (mem: guid); |
191 | g_assert_no_error (error); |
192 | g_object_unref (object: 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 | object_path: "/pokee" , |
200 | interface_info: (GDBusInterfaceInfo *) &pokee_object_info, |
201 | vtable: &pokee_vtable, |
202 | NULL, /* user_data */ |
203 | NULL, /* user_data_free_func */ |
204 | error: &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 (usec_timeout: 0, test_trap_flags: 0)) |
223 | { |
224 | /* parent */ |
225 | g_object_unref (object: streams[0]); |
226 | g_object_unref (object: 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 (stream: streams[0], NULL, error: &error); |
242 | g_assert_no_error (error); |
243 | g_assert (ok); |
244 | g_object_unref (object: streams[0]); |
245 | |
246 | connection = g_dbus_connection_new_sync (stream: streams[1], |
247 | NULL, /* guid */ |
248 | flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, |
249 | NULL, /* GDBusAuthObserver */ |
250 | NULL, |
251 | error: &error); |
252 | g_assert_no_error (error); |
253 | g_object_unref (object: streams[1]); |
254 | |
255 | /* poke the first child */ |
256 | error = NULL; |
257 | ret = g_dbus_connection_call_sync (connection, |
258 | NULL, /* name */ |
259 | object_path: "/pokee" , |
260 | interface_name: "org.gtk.GDBus.Pokee" , |
261 | method_name: "Poke" , |
262 | parameters: g_variant_new (format_string: "(s)" , "I am the POKER!" ), |
263 | G_VARIANT_TYPE ("(s)" ), /* return type */ |
264 | flags: G_DBUS_CALL_FLAGS_NONE, |
265 | timeout_msec: -1, |
266 | NULL, /* cancellable */ |
267 | error: &error); |
268 | g_assert_no_error (error); |
269 | g_variant_get (value: ret, format_string: "(&s)" , &str); |
270 | g_assert_cmpstr (str, ==, "You poked me with: 'I am the POKER!'" ); |
271 | g_variant_unref (value: ret); |
272 | |
273 | g_object_unref (object: connection); |
274 | g_main_loop_unref (loop); |
275 | exit (status: 0); |
276 | } |
277 | |
278 | #else /* G_OS_UNIX */ |
279 | |
280 | static void |
281 | test_non_socket (void) |
282 | { |
283 | /* TODO: test this with e.g. GWin32InputStream/GWin32OutputStream */ |
284 | } |
285 | #endif |
286 | |
287 | /* ---------------------------------------------------------------------------------------------------- */ |
288 | |
289 | int |
290 | main (int argc, |
291 | char *argv[]) |
292 | { |
293 | gint ret; |
294 | |
295 | g_test_init (argc: &argc, argv: &argv, NULL); |
296 | |
297 | g_test_add_func (testpath: "/gdbus/non-socket" , test_func: test_non_socket); |
298 | |
299 | ret = g_test_run(); |
300 | |
301 | return ret; |
302 | } |
303 | |