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