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 "gdbus-tests.h" |
26 | |
27 | /* all tests rely on a shared mainloop */ |
28 | static GMainLoop *loop = NULL; |
29 | |
30 | /* ---------------------------------------------------------------------------------------------------- */ |
31 | /* Test introspection parser */ |
32 | /* ---------------------------------------------------------------------------------------------------- */ |
33 | |
34 | static void |
35 | test_introspection (GDBusProxy *proxy) |
36 | { |
37 | GError *error; |
38 | const gchar *xml_data; |
39 | GDBusNodeInfo *node_info; |
40 | GDBusInterfaceInfo *interface_info; |
41 | GDBusMethodInfo *method_info; |
42 | GDBusSignalInfo *signal_info; |
43 | GVariant *result; |
44 | |
45 | error = NULL; |
46 | |
47 | /* |
48 | * Invoke Introspect(), then parse the output. |
49 | */ |
50 | result = g_dbus_proxy_call_sync (proxy, |
51 | method_name: "org.freedesktop.DBus.Introspectable.Introspect" , |
52 | NULL, |
53 | flags: G_DBUS_CALL_FLAGS_NONE, |
54 | timeout_msec: -1, |
55 | NULL, |
56 | error: &error); |
57 | g_assert_no_error (error); |
58 | g_assert (result != NULL); |
59 | g_variant_get (value: result, format_string: "(&s)" , &xml_data); |
60 | |
61 | node_info = g_dbus_node_info_new_for_xml (xml_data, error: &error); |
62 | g_assert_no_error (error); |
63 | g_assert (node_info != NULL); |
64 | |
65 | /* for now we only check a couple of things. TODO: check more things */ |
66 | |
67 | interface_info = g_dbus_node_info_lookup_interface (info: node_info, name: "com.example.NonExistantInterface" ); |
68 | g_assert (interface_info == NULL); |
69 | |
70 | interface_info = g_dbus_node_info_lookup_interface (info: node_info, name: "org.freedesktop.DBus.Introspectable" ); |
71 | g_assert (interface_info != NULL); |
72 | method_info = g_dbus_interface_info_lookup_method (info: interface_info, name: "NonExistantMethod" ); |
73 | g_assert (method_info == NULL); |
74 | method_info = g_dbus_interface_info_lookup_method (info: interface_info, name: "Introspect" ); |
75 | g_assert (method_info != NULL); |
76 | g_assert (method_info->in_args != NULL); |
77 | g_assert (method_info->in_args[0] == NULL); |
78 | g_assert (method_info->out_args != NULL); |
79 | g_assert (method_info->out_args[0] != NULL); |
80 | g_assert (method_info->out_args[1] == NULL); |
81 | g_assert_cmpstr (method_info->out_args[0]->signature, ==, "s" ); |
82 | |
83 | interface_info = g_dbus_node_info_lookup_interface (info: node_info, name: "com.example.Frob" ); |
84 | g_assert (interface_info != NULL); |
85 | signal_info = g_dbus_interface_info_lookup_signal (info: interface_info, name: "TestSignal" ); |
86 | g_assert (signal_info != NULL); |
87 | g_assert (signal_info->args != NULL); |
88 | g_assert (signal_info->args[0] != NULL); |
89 | g_assert_cmpstr (signal_info->args[0]->signature, ==, "s" ); |
90 | g_assert (signal_info->args[1] != NULL); |
91 | g_assert_cmpstr (signal_info->args[1]->signature, ==, "o" ); |
92 | g_assert (signal_info->args[2] != NULL); |
93 | g_assert_cmpstr (signal_info->args[2]->signature, ==, "v" ); |
94 | g_assert (signal_info->args[3] == NULL); |
95 | |
96 | g_dbus_node_info_unref (info: node_info); |
97 | g_variant_unref (value: result); |
98 | |
99 | g_main_loop_quit (loop); |
100 | } |
101 | |
102 | static void |
103 | test_introspection_parser (void) |
104 | { |
105 | GDBusProxy *proxy; |
106 | GDBusConnection *connection; |
107 | GError *error; |
108 | |
109 | error = NULL; |
110 | connection = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, |
111 | NULL, |
112 | error: &error); |
113 | g_assert_no_error (error); |
114 | error = NULL; |
115 | proxy = g_dbus_proxy_new_sync (connection, |
116 | flags: G_DBUS_PROXY_FLAGS_NONE, |
117 | NULL, /* GDBusInterfaceInfo */ |
118 | name: "com.example.TestService" , /* name */ |
119 | object_path: "/com/example/TestObject" , /* object path */ |
120 | interface_name: "com.example.Frob" , /* interface */ |
121 | NULL, /* GCancellable */ |
122 | error: &error); |
123 | g_assert_no_error (error); |
124 | |
125 | /* this is safe; testserver will exit once the bus goes away */ |
126 | g_assert (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver" , NULL), NULL)); |
127 | |
128 | _g_assert_property_notify (proxy, "g-name-owner" ); |
129 | |
130 | test_introspection (proxy); |
131 | |
132 | g_object_unref (object: proxy); |
133 | g_object_unref (object: connection); |
134 | } |
135 | |
136 | /* check that a parse-generate roundtrip produces identical results |
137 | */ |
138 | static void |
139 | test_generate (void) |
140 | { |
141 | GDBusNodeInfo *info; |
142 | GDBusNodeInfo *info2; |
143 | GDBusInterfaceInfo *iinfo; |
144 | GDBusMethodInfo *minfo; |
145 | GDBusSignalInfo *sinfo; |
146 | GDBusArgInfo *arginfo; |
147 | GDBusPropertyInfo *pinfo; |
148 | GDBusAnnotationInfo *aninfo; |
149 | const gchar *data = |
150 | " <node>" |
151 | " <interface name='com.example.Frob'>" |
152 | " <annotation name='foo' value='bar'/>" |
153 | " <method name='PairReturn'>" |
154 | " <annotation name='org.freedesktop.DBus.GLib.Async' value=''/>" |
155 | " <arg type='u' name='somenumber' direction='in'/>" |
156 | " <arg type='s' name='somestring' direction='out'/>" |
157 | " </method>" |
158 | " <signal name='HelloWorld'>" |
159 | " <arg type='s' name='greeting' direction='out'/>" |
160 | " </signal>" |
161 | " <method name='Sleep'>" |
162 | " <arg type='i' name='timeout' direction='in'/>" |
163 | " </method>" |
164 | " <property name='y' type='y' access='readwrite'>" |
165 | " <annotation name='needs-escaping' value='bar<>'"'/>" |
166 | " </property>" |
167 | " </interface>" |
168 | " </node>" ; |
169 | |
170 | GString *string; |
171 | GString *string2; |
172 | GError *error; |
173 | |
174 | error = NULL; |
175 | info = g_dbus_node_info_new_for_xml (xml_data: data, error: &error); |
176 | g_assert_no_error (error); |
177 | |
178 | iinfo = g_dbus_node_info_lookup_interface (info, name: "com.example.Frob" ); |
179 | aninfo = iinfo->annotations[0]; |
180 | g_assert_cmpstr (aninfo->key, ==, "foo" ); |
181 | g_assert_cmpstr (aninfo->value, ==, "bar" ); |
182 | g_assert (iinfo->annotations[1] == NULL); |
183 | minfo = g_dbus_interface_info_lookup_method (info: iinfo, name: "PairReturn" ); |
184 | g_assert_cmpstr (g_dbus_annotation_info_lookup (minfo->annotations, "org.freedesktop.DBus.GLib.Async" ), ==, "" ); |
185 | arginfo = minfo->in_args[0]; |
186 | g_assert_cmpstr (arginfo->name, ==, "somenumber" ); |
187 | g_assert_cmpstr (arginfo->signature, ==, "u" ); |
188 | g_assert (minfo->in_args[1] == NULL); |
189 | arginfo = minfo->out_args[0]; |
190 | g_assert_cmpstr (arginfo->name, ==, "somestring" ); |
191 | g_assert_cmpstr (arginfo->signature, ==, "s" ); |
192 | g_assert (minfo->out_args[1] == NULL); |
193 | sinfo = g_dbus_interface_info_lookup_signal (info: iinfo, name: "HelloWorld" ); |
194 | arginfo = sinfo->args[0]; |
195 | g_assert_cmpstr (arginfo->name, ==, "greeting" ); |
196 | g_assert_cmpstr (arginfo->signature, ==, "s" ); |
197 | g_assert (sinfo->args[1] == NULL); |
198 | pinfo = g_dbus_interface_info_lookup_property (info: iinfo, name: "y" ); |
199 | g_assert_cmpstr (pinfo->signature, ==, "y" ); |
200 | g_assert_cmpint (pinfo->flags, ==, G_DBUS_PROPERTY_INFO_FLAGS_READABLE | |
201 | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE); |
202 | |
203 | string = g_string_new (init: "" ); |
204 | g_dbus_node_info_generate_xml (info, indent: 2, string_builder: string); |
205 | |
206 | info2 = g_dbus_node_info_new_for_xml (xml_data: string->str, error: &error); |
207 | string2 = g_string_new (init: "" ); |
208 | g_dbus_node_info_generate_xml (info: info2, indent: 2, string_builder: string2); |
209 | |
210 | g_assert_cmpstr (string->str, ==, string2->str); |
211 | g_string_free (string, TRUE); |
212 | g_string_free (string: string2, TRUE); |
213 | |
214 | g_dbus_node_info_unref (info); |
215 | g_dbus_node_info_unref (info: info2); |
216 | } |
217 | |
218 | /* test that omitted direction attributes default to 'out' for signals, |
219 | * and 'in' for methods. |
220 | */ |
221 | static void |
222 | test_default_direction (void) |
223 | { |
224 | GDBusNodeInfo *info; |
225 | GDBusInterfaceInfo *iinfo; |
226 | GDBusMethodInfo *minfo; |
227 | GDBusSignalInfo *sinfo; |
228 | GDBusArgInfo *arginfo; |
229 | const gchar *data = |
230 | " <node>" |
231 | " <interface name='com.example.Frob'>" |
232 | " <signal name='HelloWorld'>" |
233 | " <arg type='s' name='greeting'/>" |
234 | " </signal>" |
235 | " <method name='Sleep'>" |
236 | " <arg type='i' name='timeout'/>" |
237 | " </method>" |
238 | " </interface>" |
239 | " </node>" ; |
240 | |
241 | GError *error; |
242 | |
243 | error = NULL; |
244 | info = g_dbus_node_info_new_for_xml (xml_data: data, error: &error); |
245 | g_assert_no_error (error); |
246 | |
247 | iinfo = g_dbus_node_info_lookup_interface (info, name: "com.example.Frob" ); |
248 | sinfo = g_dbus_interface_info_lookup_signal (info: iinfo, name: "HelloWorld" ); |
249 | g_assert (sinfo->args != NULL); |
250 | arginfo = sinfo->args[0]; |
251 | g_assert_cmpstr (arginfo->name, ==, "greeting" ); |
252 | g_assert (sinfo->args[1] == NULL); |
253 | minfo = g_dbus_interface_info_lookup_method (info: iinfo, name: "Sleep" ); |
254 | g_assert (minfo->in_args != NULL); |
255 | arginfo = minfo->in_args[0]; |
256 | g_assert_cmpstr (arginfo->name, ==, "timeout" ); |
257 | g_assert (minfo->in_args[1] == NULL); |
258 | |
259 | g_dbus_node_info_unref (info); |
260 | } |
261 | |
262 | static void |
263 | (void) |
264 | { |
265 | GDBusNodeInfo *info; |
266 | const gchar *data = |
267 | " <node>" |
268 | " <interface name='com.example.Frob' version='1.0'>" |
269 | " <doc:doc><doc:description><doc:para>Blah blah</doc:para></doc:description></doc:doc>" |
270 | " <method name='DownloadPackages'>" |
271 | " <arg type='u' name='somenumber' direction='in'>" |
272 | " <doc:doc><doc:summary><doc:para>" |
273 | " See <doc:ulink url='http:///example.com'>example</doc:ulink>" |
274 | " </doc:para></doc:summary></doc:doc>" |
275 | " </arg>" |
276 | " <arg type='s' name='somestring' direction='out'>" |
277 | " <doc:doc><doc:summary><doc:para>" |
278 | " More docs" |
279 | " </doc:para></doc:summary></doc:doc>" |
280 | " </arg>" |
281 | " </method>" |
282 | " <signal name='HelloWorld'>" |
283 | " <arg type='s' name='somestring'/>" |
284 | " </signal>" |
285 | " <method name='Sleep'>" |
286 | " <arg type='i' name='timeout' direction='in'/>" |
287 | " </method>" |
288 | " <property name='y' type='y' access='readwrite'/>" |
289 | " </interface>" |
290 | " </node>" ; |
291 | GError *error; |
292 | |
293 | error = NULL; |
294 | info = g_dbus_node_info_new_for_xml (xml_data: data, error: &error); |
295 | g_assert_no_error (error); |
296 | |
297 | g_dbus_node_info_unref (info); |
298 | } |
299 | |
300 | /* ---------------------------------------------------------------------------------------------------- */ |
301 | |
302 | int |
303 | main (int argc, |
304 | char *argv[]) |
305 | { |
306 | gint ret; |
307 | |
308 | g_test_init (argc: &argc, argv: &argv, NULL); |
309 | |
310 | /* all the tests rely on a shared main loop */ |
311 | loop = g_main_loop_new (NULL, FALSE); |
312 | |
313 | g_test_add_func (testpath: "/gdbus/introspection-parser" , test_func: test_introspection_parser); |
314 | g_test_add_func (testpath: "/gdbus/introspection-generate" , test_func: test_generate); |
315 | g_test_add_func (testpath: "/gdbus/introspection-default-direction" , test_func: test_default_direction); |
316 | g_test_add_func (testpath: "/gdbus/introspection-extra-data" , test_func: test_extra_data); |
317 | |
318 | ret = session_bus_run (); |
319 | |
320 | while (g_main_context_iteration (NULL, FALSE)); |
321 | g_main_loop_unref (loop); |
322 | |
323 | return ret; |
324 | } |
325 | |