Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright © 2022 Endless OS Foundation, LLC
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 : : * SPDX-License-Identifier: LGPL-2.1-or-later
21 : : */
22 : :
23 : : #include <gio/gio.h>
24 : : #include <gio/gunixfdlist.h>
25 : : #include <string.h>
26 : : #include <unistd.h>
27 : :
28 : : #include "gdbus-tests.h"
29 : :
30 : : static const GDBusArgInfo foo_get_fds_in_args =
31 : : {
32 : : -1,
33 : : "type",
34 : : "s",
35 : : NULL
36 : : };
37 : : static const GDBusArgInfo * const foo_get_fds_in_arg_pointers[] = {&foo_get_fds_in_args, NULL};
38 : :
39 : : static const GDBusArgInfo foo_get_fds_out_args =
40 : : {
41 : : -1,
42 : : "some_fd",
43 : : "h",
44 : : NULL
45 : : };
46 : : static const GDBusArgInfo * const foo_get_fds_out_arg_pointers[] = {&foo_get_fds_out_args, NULL};
47 : :
48 : : static const GDBusMethodInfo foo_method_info_wrong_return_type =
49 : : {
50 : : -1,
51 : : "WrongReturnType",
52 : : NULL, /* in args */
53 : : NULL, /* out args */
54 : : NULL /* annotations */
55 : : };
56 : : static const GDBusMethodInfo foo_method_info_close_before_returning =
57 : : {
58 : : -1,
59 : : "CloseBeforeReturning",
60 : : NULL, /* in args */
61 : : NULL, /* out args */
62 : : NULL /* annotations */
63 : : };
64 : : static const GDBusMethodInfo foo_method_info_get_fds =
65 : : {
66 : : -1,
67 : : "GetFDs",
68 : : (GDBusArgInfo **) foo_get_fds_in_arg_pointers,
69 : : (GDBusArgInfo **) foo_get_fds_out_arg_pointers,
70 : : NULL /* annotations */
71 : : };
72 : : static const GDBusMethodInfo foo_method_info_return_error =
73 : : {
74 : : -1,
75 : : "ReturnError",
76 : : NULL, /* in args */
77 : : NULL, /* out args */
78 : : NULL /* annotations */
79 : : };
80 : : static const GDBusMethodInfo * const foo_method_info_pointers[] = {
81 : : &foo_method_info_wrong_return_type,
82 : : &foo_method_info_close_before_returning,
83 : : &foo_method_info_get_fds,
84 : : &foo_method_info_return_error,
85 : : NULL
86 : : };
87 : :
88 : : static const GDBusPropertyInfo foo_property_info[] =
89 : : {
90 : : {
91 : : -1,
92 : : "InvalidType",
93 : : "s",
94 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
95 : : NULL
96 : : },
97 : : {
98 : : -1,
99 : : "InvalidTypeNull",
100 : : "s",
101 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
102 : : NULL
103 : : },
104 : : {
105 : : -1,
106 : : "InvalidValueType",
107 : : "s",
108 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
109 : : NULL
110 : : },
111 : : };
112 : : static const GDBusPropertyInfo * const foo_property_info_pointers[] =
113 : : {
114 : : &foo_property_info[0],
115 : : &foo_property_info[1],
116 : : &foo_property_info[2],
117 : : NULL
118 : : };
119 : :
120 : : static const GDBusInterfaceInfo foo_interface_info =
121 : : {
122 : : -1,
123 : : "org.example.Foo",
124 : : (GDBusMethodInfo **) &foo_method_info_pointers,
125 : : NULL, /* signals */
126 : : (GDBusPropertyInfo **) &foo_property_info_pointers,
127 : : NULL, /* annotations */
128 : : };
129 : :
130 : : /* ---------------------------------------------------------------------------------------------------- */
131 : :
132 : : static void
133 : 20 : test_method_invocation_return_method_call (GDBusConnection *connection,
134 : : const gchar *sender,
135 : : const gchar *object_path,
136 : : const gchar *interface_name,
137 : : const gchar *method_name,
138 : : GVariant *parameters,
139 : : GDBusMethodInvocation *invocation,
140 : : gpointer user_data)
141 : : {
142 : 20 : gboolean no_reply = g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED;
143 : :
144 [ + + ]: 20 : if (g_str_equal (interface_name, "org.freedesktop.DBus.Properties") &&
145 [ + + ]: 10 : g_str_equal (method_name, "Get"))
146 : 6 : {
147 : : const gchar *iface_name, *prop_name;
148 : :
149 : 6 : g_variant_get (parameters, "(&s&s)", &iface_name, &prop_name);
150 : 6 : g_assert_cmpstr (iface_name, ==, "org.example.Foo");
151 : :
152 : : /* Do different things depending on the property name. */
153 [ + + ]: 6 : if (g_str_equal (prop_name, "InvalidType"))
154 : : {
155 [ + + ]: 2 : if (!no_reply)
156 : 1 : g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
157 : : "Type of return value for property 'Get' call should be '(v)' but got '(s)'");
158 : 2 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "this type is invalid"));
159 : : }
160 [ + + ]: 4 : else if (g_str_equal (prop_name, "InvalidTypeNull"))
161 : : {
162 [ + + ]: 2 : if (!no_reply)
163 : 1 : g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
164 : : "Type of return value for property 'Get' call should be '(v)' but got '()'");
165 : 2 : g_dbus_method_invocation_return_value (invocation, NULL);
166 : : }
167 [ + - ]: 2 : else if (g_str_equal (prop_name, "InvalidValueType"))
168 : : {
169 [ + + ]: 2 : if (!no_reply)
170 : 1 : g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
171 : : "Value returned from property 'Get' call for 'InvalidValueType' should be 's' but is 'u'");
172 : 2 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("(v)", g_variant_new_uint32 (123)));
173 : : }
174 : : else
175 : : {
176 : : g_assert_not_reached ();
177 : : }
178 : :
179 : 6 : g_test_assert_expected_messages ();
180 : : }
181 [ + + ]: 14 : else if (g_str_equal (interface_name, "org.freedesktop.DBus.Properties") &&
182 [ + + ]: 4 : g_str_equal (method_name, "Set"))
183 : 2 : {
184 : : const gchar *iface_name, *prop_name;
185 : : GVariant *value;
186 : :
187 : 2 : g_variant_get (parameters, "(&s&sv)", &iface_name, &prop_name, &value);
188 : 2 : g_assert_cmpstr (iface_name, ==, "org.example.Foo");
189 : :
190 [ + - ]: 2 : if (g_str_equal (prop_name, "InvalidType"))
191 : : {
192 [ + + ]: 2 : if (!no_reply)
193 : 1 : g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
194 : : "Type of return value for property 'Set' call should be '()' but got '(s)'");
195 : 2 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "should be unit"));
196 : : }
197 : : else
198 : : {
199 : : g_assert_not_reached ();
200 : : }
201 : :
202 : 2 : g_test_assert_expected_messages ();
203 : 2 : g_variant_unref (value);
204 : : }
205 [ + + ]: 12 : else if (g_str_equal (interface_name, "org.freedesktop.DBus.Properties") &&
206 [ + - ]: 2 : g_str_equal (method_name, "GetAll"))
207 : 2 : {
208 : : const gchar *iface_name;
209 : :
210 : 2 : g_variant_get (parameters, "(&s)", &iface_name);
211 : 2 : g_assert_cmpstr (iface_name, ==, "org.example.Foo");
212 : :
213 [ + + ]: 2 : if (!no_reply)
214 : 1 : g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
215 : : "Type of return value for property 'GetAll' call should be '(a{sv})' but got '(s)'");
216 : 2 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "should be a different type"));
217 : : }
218 [ + - ]: 10 : else if (g_str_equal (interface_name, "org.example.Foo") &&
219 [ + + ]: 10 : g_str_equal (method_name, "WrongReturnType"))
220 : : {
221 [ + + ]: 2 : if (!no_reply)
222 : 1 : g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
223 : : "Type of return value is incorrect: expected '()', got '(s)'");
224 : 2 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "should be a different type"));
225 : : }
226 [ + - ]: 8 : else if (g_str_equal (interface_name, "org.example.Foo") &&
227 [ + + ]: 8 : g_str_equal (method_name, "CloseBeforeReturning"))
228 : : {
229 : 2 : g_dbus_connection_close (connection, NULL, NULL, NULL);
230 : :
231 : 2 : g_dbus_method_invocation_return_value (invocation, NULL);
232 : : }
233 [ + - ]: 6 : else if (g_str_equal (interface_name, "org.example.Foo") &&
234 [ + + ]: 6 : g_str_equal (method_name, "GetFDs"))
235 : 4 : {
236 : : const gchar *action;
237 : 4 : GUnixFDList *list = NULL;
238 : 4 : GError *local_error = NULL;
239 : :
240 : 4 : g_variant_get (parameters, "(&s)", &action);
241 : :
242 : 4 : list = g_unix_fd_list_new ();
243 : 4 : g_unix_fd_list_append (list, 1, &local_error);
244 : 4 : g_assert_no_error (local_error);
245 : :
246 [ + + ]: 4 : if (g_str_equal (action, "WrongNumber"))
247 : : {
248 : 2 : g_unix_fd_list_append (list, 1, &local_error);
249 : 2 : g_assert_no_error (local_error);
250 : : }
251 : :
252 [ + + ]: 4 : if (g_str_equal (action, "Valid") ||
253 [ + - ]: 2 : g_str_equal (action, "WrongNumber"))
254 : 4 : g_dbus_method_invocation_return_value_with_unix_fd_list (invocation, g_variant_new ("(h)", 0), list);
255 : : else
256 : : g_assert_not_reached ();
257 : :
258 : 4 : g_object_unref (list);
259 : : }
260 [ + - ]: 2 : else if (g_str_equal (interface_name, "org.example.Foo") &&
261 [ + - ]: 2 : g_str_equal (method_name, "ReturnError"))
262 : : {
263 : 2 : g_dbus_method_invocation_return_dbus_error (invocation, "org.example.Foo", "SomeError");
264 : : }
265 : : else
266 : : g_assert_not_reached ();
267 : 20 : }
268 : :
269 : : static void
270 : 10 : ensure_result_cb (GObject *source,
271 : : GAsyncResult *result,
272 : : gpointer user_data)
273 : : {
274 : 10 : GDBusConnection *connection = G_DBUS_CONNECTION (source);
275 : : GVariant *reply;
276 : 10 : guint *n_outstanding_calls = user_data;
277 : :
278 : 10 : reply = g_dbus_connection_call_finish (connection, result, NULL);
279 : :
280 : : /* We don’t care what the reply is. */
281 : 10 : g_clear_pointer (&reply, g_variant_unref);
282 : :
283 : 10 : g_assert_cmpint (*n_outstanding_calls, >, 0);
284 : 10 : *n_outstanding_calls = *n_outstanding_calls - 1;
285 : 10 : }
286 : :
287 : : static void
288 : 1 : test_method_invocation_return (void)
289 : : {
290 : 1 : GDBusConnection *connection = NULL;
291 : 1 : GError *local_error = NULL;
292 : : guint registration_id;
293 : 1 : const GDBusInterfaceVTable vtable = {
294 : : test_method_invocation_return_method_call, NULL, NULL, { 0 }
295 : : };
296 : 1 : guint n_outstanding_calls = 0;
297 : :
298 : 1 : g_test_summary ("Test calling g_dbus_method_invocation_return_*() in various ways");
299 : :
300 : : /* Connect to the bus. */
301 : 1 : connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
302 : 1 : g_assert_no_error (local_error);
303 : 1 : g_assert_nonnull (connection);
304 : :
305 : : /* Don’t exit the test when the server closes the connection in
306 : : * CloseBeforeReturning(). */
307 : 1 : g_dbus_connection_set_exit_on_close (connection, FALSE);
308 : :
309 : : /* Register an object which we can call methods on. */
310 : 1 : registration_id = g_dbus_connection_register_object (connection,
311 : : "/foo",
312 : : (GDBusInterfaceInfo *) &foo_interface_info,
313 : : &vtable, NULL, NULL, &local_error);
314 : 1 : g_assert_no_error (local_error);
315 : 1 : g_assert_cmpint (registration_id, !=, 0);
316 : :
317 : : /* Test a variety of error cases */
318 : : {
319 : : const struct
320 : : {
321 : : const gchar *interface_name;
322 : : const gchar *method_name;
323 : : const gchar *parameters_string;
324 : : gboolean tests_undefined_behaviour;
325 : : }
326 : 1 : calls[] =
327 : : {
328 : : { "org.freedesktop.DBus.Properties", "Get", "('org.example.Foo', 'InvalidType')", TRUE },
329 : : { "org.freedesktop.DBus.Properties", "Get", "('org.example.Foo', 'InvalidTypeNull')", TRUE },
330 : : { "org.freedesktop.DBus.Properties", "Get", "('org.example.Foo', 'InvalidValueType')", TRUE },
331 : : { "org.freedesktop.DBus.Properties", "Set", "('org.example.Foo', 'InvalidType', <'irrelevant'>)", TRUE },
332 : : { "org.freedesktop.DBus.Properties", "GetAll", "('org.example.Foo',)", TRUE },
333 : : { "org.example.Foo", "WrongReturnType", "()", TRUE },
334 : : { "org.example.Foo", "GetFDs", "('Valid',)", FALSE },
335 : : { "org.example.Foo", "GetFDs", "('WrongNumber',)", TRUE },
336 : : { "org.example.Foo", "ReturnError", "()", FALSE },
337 : : { "org.example.Foo", "CloseBeforeReturning", "()", FALSE },
338 : : };
339 : : gsize i;
340 : :
341 [ + + ]: 11 : for (i = 0; i < G_N_ELEMENTS (calls); i++)
342 : : {
343 [ + + - + ]: 10 : if (calls[i].tests_undefined_behaviour && !g_test_undefined ())
344 : : {
345 : 0 : g_test_message ("Skipping %s.%s", calls[i].interface_name, calls[i].method_name);
346 : 0 : continue;
347 : : }
348 : : else
349 : : {
350 : 10 : g_test_message ("Calling %s.%s", calls[i].interface_name, calls[i].method_name);
351 : : }
352 : :
353 : : /* Call twice, once expecting a result and once not. Do the call which
354 : : * doesn’t expect a result first; message ordering should ensure that
355 : : * it’s completed by the time the second call completes, so we don’t
356 : : * have to account for it separately.
357 : : *
358 : : * That’s good, because the only way to get g_dbus_connection_call()
359 : : * to set %G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED is to not provide
360 : : * a callback function. */
361 : 10 : n_outstanding_calls++;
362 : :
363 : 10 : g_dbus_connection_call (connection,
364 : : g_dbus_connection_get_unique_name (connection),
365 : : "/foo",
366 : 10 : calls[i].interface_name,
367 : 10 : calls[i].method_name,
368 : 10 : g_variant_new_parsed (calls[i].parameters_string),
369 : : NULL,
370 : : G_DBUS_CALL_FLAGS_NONE,
371 : : -1,
372 : : NULL,
373 : : NULL, /* no callback */
374 : : NULL);
375 : :
376 : 10 : g_dbus_connection_call (connection,
377 : : g_dbus_connection_get_unique_name (connection),
378 : : "/foo",
379 : 10 : calls[i].interface_name,
380 : 10 : calls[i].method_name,
381 : 10 : g_variant_new_parsed (calls[i].parameters_string),
382 : : NULL,
383 : : G_DBUS_CALL_FLAGS_NONE,
384 : : -1,
385 : : NULL,
386 : : ensure_result_cb,
387 : : &n_outstanding_calls);
388 : : }
389 : : }
390 : :
391 : : /* Wait until all the calls are complete. */
392 [ + + ]: 13 : while (n_outstanding_calls > 0)
393 : 12 : g_main_context_iteration (NULL, TRUE);
394 : :
395 : 1 : g_dbus_connection_unregister_object (connection, registration_id);
396 : 1 : g_object_unref (connection);
397 : 1 : }
398 : :
399 : : int
400 : 1 : main (int argc,
401 : : char *argv[])
402 : : {
403 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
404 : :
405 : 1 : g_test_add_func ("/gdbus/method-invocation/return", test_method_invocation_return);
406 : :
407 : 1 : return session_bus_run ();
408 : : }
|