[unit] Add a debug print and some more Dict test cases
[browser-dbus-bridge.git] / tests / unit.c
1 /**
2  * Unit, a D-Bus tester for argument marshalling
3  *
4  * Copyright © 2008 Movial Creative Technologies Inc
5  *  Contact: Movial Creative Technologies Inc, <info@movial.com>
6  *  Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23 /*
24  * The Unit interface has methods that take a certain argument types and
25  * send a reply and a subsequent signal with the received arguments.
26  *
27  * To build, execute this:
28  *
29  *   gcc -o unit unit.c $(pkg-config --cflags --libs dbus-glib-1)
30  *
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <dbus/dbus.h>
38 #include <dbus/dbus-glib-lowlevel.h>
39
40 #define OBJECT_PATH "/org/movial/Unit"
41 #define SERVICE_NAME "org.movial.Unit"
42
43 GMainLoop *loop;
44
45 static gboolean u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
46 static gboolean u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
47 static gboolean u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
48 static gboolean u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
49
50 static void
51 u_unregister(DBusConnection *connection, void *user_data)
52 {
53   g_debug("Object path unregistered");
54   g_main_loop_quit(loop);
55 }
56
57 static void
58 u_transfer_arg(DBusMessageIter *to, DBusMessageIter *from)
59 {
60   dbus_uint64_t value;
61   int type = dbus_message_iter_get_arg_type(from);
62   
63   if (!dbus_type_is_basic(type))
64   {
65     switch (type)
66     {
67       case DBUS_TYPE_ARRAY:
68         u_transfer_array(to, from);
69         break;
70       case DBUS_TYPE_VARIANT:
71         u_transfer_variant(to, from);
72         break;
73       case DBUS_TYPE_DICT_ENTRY:
74         u_transfer_dict(to, from);
75         break;
76       case DBUS_TYPE_STRUCT:
77         u_transfer_struct(to, from);
78         break;
79       default:
80         g_warning("Non-basic type '%c' in variants not yet handled", type);
81         break;
82     }
83     return;
84   }
85
86   dbus_message_iter_get_basic(from, &value);
87   if (type == DBUS_TYPE_STRING)
88   {
89     g_debug("Transferring string arg %s", value);
90   }
91   if (type == DBUS_TYPE_INT32)
92   {
93     g_debug("Transferring int arg %i", value);
94   }
95   if (type == DBUS_TYPE_DOUBLE)
96   {
97     g_debug("Transferring double arg %f", value);
98   }
99   dbus_message_iter_append_basic(to, type, &value);
100 }
101
102 static gboolean
103 u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
104 {
105   DBusMessageIter to;
106   DBusMessageIter from;
107   const char *sig = NULL;
108   
109   dbus_message_iter_recurse(from_iter, &from);
110   sig = dbus_message_iter_get_signature(&from);
111
112   if (sig == NULL)
113     return FALSE;
114
115   dbus_message_iter_open_container(to_iter, DBUS_TYPE_VARIANT, sig, &to);
116
117   do {
118     u_transfer_arg(&to, &from);
119   } while (dbus_message_iter_next(&from));
120   
121   dbus_message_iter_close_container(to_iter, &to);
122   
123   return TRUE;
124 }
125
126 static gboolean
127 u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
128 {
129   DBusMessageIter to;
130   DBusMessageIter from;
131   int elem_type;
132   char *sig = NULL;
133   
134   dbus_message_iter_recurse(from_iter, &from);
135
136   elem_type = dbus_message_iter_get_element_type(from_iter);
137   switch (elem_type)
138   {
139     case DBUS_TYPE_ARRAY:
140       {
141         /* Fetch the element type for this too */
142         sig = g_strdup(dbus_message_iter_get_signature(from_iter)+1);
143         break;
144       }
145     case DBUS_TYPE_DICT_ENTRY:
146     case DBUS_TYPE_STRUCT:
147       g_warning("Element type '%c' in arrays not supported", elem_type);
148       break;
149     default:
150       sig = g_strdup_printf("%c", dbus_message_iter_get_element_type(from_iter));
151       break;
152   }
153
154   if (sig == NULL)
155     return FALSE;
156
157   dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
158
159   do {
160     u_transfer_arg(&to, &from);
161   } while (dbus_message_iter_next(&from));
162   
163   dbus_message_iter_close_container(to_iter, &to);
164
165   return TRUE;
166 }
167
168 static gboolean
169 u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
170 {
171   DBusMessageIter to;
172   DBusMessageIter from;
173   char *sig = NULL;
174
175   dbus_message_iter_recurse(from_iter, &from);
176   sig = g_strdup(dbus_message_iter_get_signature(&from));
177
178   if (sig == NULL)
179     return FALSE;
180
181   dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
182   do {
183     DBusMessageIter subfrom;
184     DBusMessageIter dictiter;
185     dbus_message_iter_open_container(&to, DBUS_TYPE_DICT_ENTRY,
186                                                     NULL, &dictiter);
187     dbus_message_iter_recurse(&from, &subfrom);
188
189     u_transfer_arg(&dictiter, &subfrom);
190     if (dbus_message_iter_next(&subfrom)) {
191       u_transfer_arg(&dictiter, &subfrom);
192     }
193     dbus_message_iter_close_container(&to, &dictiter);
194   } while (dbus_message_iter_next(&from));
195   
196   dbus_message_iter_close_container(to_iter, &to);
197   
198   return TRUE;
199 }
200
201 static gboolean
202 u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
203 {
204   DBusMessageIter to;
205   DBusMessageIter from;
206
207   dbus_message_iter_recurse(from_iter, &from);
208   dbus_message_iter_open_container(to_iter, DBUS_TYPE_STRUCT, NULL, &to);
209
210   do {
211     u_transfer_arg(&to, &from);
212   } while (dbus_message_iter_next(&from));
213   
214   dbus_message_iter_close_container(to_iter, &to);
215   
216   return TRUE;
217 }
218
219 static DBusHandlerResult
220 u_message(DBusConnection *connection, DBusMessage *message, void *user_data)
221 {
222 #ifdef DEBUG  
223   const char *path = dbus_message_get_path(message);
224   const char *interface = dbus_message_get_interface(message);
225   const char *member = dbus_message_get_member(message);
226   const char *signature = dbus_message_get_signature(message);
227
228   g_debug("Message received, path %s, interface %s, member %s, signature %s",
229           path == NULL ? "not set" : path,
230           interface == NULL ? "not set" : interface,
231           member == NULL ? "not set" : member,
232           signature == NULL ? "not set" : signature);
233 #endif
234   
235   if (!dbus_message_has_path(message, OBJECT_PATH))
236     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
237
238   if (dbus_message_is_method_call(message, SERVICE_NAME, "start"))
239   {
240     DBusMessage *reply;
241
242     /* TODO: start logging */
243     
244     reply = dbus_message_new_method_return(message);
245     dbus_connection_send(connection, reply, NULL);
246     dbus_message_unref(reply);
247     
248     return DBUS_HANDLER_RESULT_HANDLED;
249   }
250
251   if (dbus_message_is_method_call(message, SERVICE_NAME, "end"))
252   {
253     DBusMessage *reply;
254
255     /* TODO: stop logging */
256     
257     reply = dbus_message_new_method_return(message);
258     dbus_connection_send(connection, reply, NULL);
259     dbus_message_unref(reply);
260     
261     return DBUS_HANDLER_RESULT_HANDLED;
262   }
263
264   if (strlen(dbus_message_get_signature(message)) == 0)
265   {
266     DBusMessage *reply = dbus_message_new_error(message,
267                                                 SERVICE_NAME ".ArgError",
268                                                 "Empty signature");
269     dbus_connection_send(connection, reply, NULL);
270     dbus_message_unref(reply);
271     return DBUS_HANDLER_RESULT_HANDLED;
272   }
273
274 #define BASIC_TYPE(method_name, dtype) \
275   if (dbus_message_is_method_call(message, SERVICE_NAME, method_name)) \
276   { \
277     dbus_uint64_t value = 0; \
278     DBusError err; \
279     DBusMessage *reply = NULL; \
280     DBusMessage *signal = NULL; \
281     dbus_error_init(&err); \
282     if (!dbus_message_get_args(message, &err, \
283                                DBUS_TYPE_ ## dtype, &value, \
284                                DBUS_TYPE_INVALID)) \
285     { \
286       reply = dbus_message_new_error(message, \
287                                      SERVICE_NAME ".ArgError", \
288                                      err.message); \
289       g_debug(err.message); \
290       g_assert(reply != NULL); \
291     } else { \
292       reply = dbus_message_new_method_return(message); \
293       g_assert(reply != NULL); \
294       dbus_message_append_args(reply, \
295                                DBUS_TYPE_ ## dtype, &value, \
296                                DBUS_TYPE_INVALID); \
297       signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, method_name); \
298       g_debug("Appending %llu to signal", value); \
299       dbus_message_append_args(signal, \
300                                DBUS_TYPE_ ## dtype, &value, \
301                                DBUS_TYPE_INVALID); \
302     } \
303     dbus_connection_send(connection, reply, NULL); \
304     dbus_message_unref(reply); \
305     if (signal != NULL) \
306     { \
307       dbus_connection_send(connection, signal, NULL); \
308       dbus_message_unref(signal); \
309     } \
310     return DBUS_HANDLER_RESULT_HANDLED; \
311   }
312   
313   BASIC_TYPE("Boolean", BOOLEAN);
314   BASIC_TYPE("Byte", BYTE);
315   BASIC_TYPE("Int16", INT16);
316   BASIC_TYPE("Int32", INT32);
317   BASIC_TYPE("Int64", INT64);
318   BASIC_TYPE("UInt16", UINT16);
319   BASIC_TYPE("UInt32", UINT32);
320   BASIC_TYPE("UInt64", UINT64);
321   BASIC_TYPE("Double", DOUBLE);
322   BASIC_TYPE("String", STRING);
323   BASIC_TYPE("ObjectPath", OBJECT_PATH);
324   BASIC_TYPE("Signature", SIGNATURE);
325   
326   if (dbus_message_is_method_call(message, SERVICE_NAME, "Array"))
327   {
328     DBusMessageIter iter;
329     DBusMessageIter reply_iter;
330     DBusMessage *reply;
331     DBusMessage *signal;
332
333     dbus_message_iter_init(message, &iter);
334
335     reply = dbus_message_new_method_return(message);
336
337     dbus_message_iter_init_append(reply, &reply_iter);
338     u_transfer_array(&reply_iter, &iter);
339
340     dbus_message_iter_init(message, &iter);
341     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Array");
342     dbus_message_iter_init_append(signal, &reply_iter);
343     u_transfer_array(&reply_iter, &iter);
344
345     dbus_connection_send(connection, reply, NULL);
346     dbus_message_unref(reply);
347     dbus_connection_send(connection, signal, NULL);
348     dbus_message_unref(signal);
349     return DBUS_HANDLER_RESULT_HANDLED;
350   }
351
352   if (dbus_message_is_method_call(message, SERVICE_NAME, "Variant"))
353   {
354     DBusMessageIter iter;
355     DBusMessageIter reply_iter;
356     DBusMessage *reply;
357     DBusMessage *signal = NULL;
358
359     if (!g_str_equal(dbus_message_get_signature(message), "v"))
360     {
361       reply = dbus_message_new_error(message,
362                                      SERVICE_NAME ".ArgError",
363                                      "Signature mismatch");
364       dbus_connection_send(connection, reply, NULL);
365       dbus_message_unref(reply);
366       return DBUS_HANDLER_RESULT_HANDLED;
367     }
368     dbus_message_iter_init(message, &iter);
369
370     reply = dbus_message_new_method_return(message);
371     dbus_message_iter_init_append(reply, &reply_iter);
372     u_transfer_variant(&reply_iter, &iter);
373
374     dbus_message_iter_init(message, &iter);
375     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Variant");
376     dbus_message_iter_init_append(signal, &reply_iter);
377     u_transfer_variant(&reply_iter, &iter);
378
379     dbus_connection_send(connection, reply, NULL);
380     dbus_message_unref(reply);
381     dbus_connection_send(connection, signal, NULL);
382     dbus_message_unref(signal);
383     return DBUS_HANDLER_RESULT_HANDLED;
384   }
385
386   if (dbus_message_is_method_call(message, SERVICE_NAME, "Dict"))
387   {
388     DBusMessageIter iter;
389     DBusMessageIter reply_iter;
390     DBusMessage *reply;
391     DBusMessage *signal;
392
393     dbus_message_iter_init(message, &iter);
394
395     reply = dbus_message_new_method_return(message);
396     dbus_message_iter_init_append(reply, &reply_iter);
397
398     u_transfer_dict(&reply_iter, &iter);
399
400     dbus_message_iter_init(message, &iter);
401     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Dict");
402     dbus_message_iter_init_append(signal, &reply_iter);
403     u_transfer_dict(&reply_iter, &iter);
404
405     dbus_connection_send(connection, reply, NULL);
406     dbus_message_unref(reply);
407     dbus_connection_send(connection, signal, NULL);
408     dbus_message_unref(signal);
409     
410     return DBUS_HANDLER_RESULT_HANDLED;
411   }
412
413   if (dbus_message_is_method_call(message, SERVICE_NAME, "Struct"))
414   {
415     DBusMessageIter iter;
416     DBusMessageIter reply_iter;
417     DBusMessage *reply;
418     DBusMessage *signal;
419     const char *signature = dbus_message_get_signature(message);
420     
421     if (signature[0] != '(')
422         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
423
424     dbus_message_iter_init(message, &iter);
425
426     reply = dbus_message_new_method_return(message);
427     dbus_message_iter_init_append(reply, &reply_iter);
428
429     u_transfer_struct(&reply_iter, &iter);
430
431     dbus_message_iter_init(message, &iter);
432     signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Struct");
433     dbus_message_iter_init_append(signal, &reply_iter);
434     u_transfer_struct(&reply_iter, &iter);
435
436     dbus_connection_send(connection, reply, NULL);
437     dbus_message_unref(reply);
438     dbus_connection_send(connection, signal, NULL);
439     dbus_message_unref(signal);
440     
441     return DBUS_HANDLER_RESULT_HANDLED;
442   }
443
444 #ifdef DEBUG
445   g_debug("Didn't handle the message");
446 #endif
447
448   
449   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
450 }
451
452 int main(int argc, char **argv)
453 {
454   DBusError err;
455   DBusConnection* conn;
456   DBusObjectPathVTable vtable;
457   int ret;
458
459   dbus_error_init(&err);
460   conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
461   if (dbus_error_is_set(&err)) { 
462     g_warning("Connection Error (%s)\n", err.message); 
463     dbus_error_free(&err); 
464   }
465   g_assert(conn != NULL);
466
467   vtable.unregister_function = u_unregister;
468   vtable.message_function = u_message;
469
470   if (!dbus_connection_register_object_path(conn, OBJECT_PATH,
471                                             &vtable, NULL))
472   {
473     g_error("Could not register object path");
474   }
475
476   ret = dbus_bus_request_name(conn, SERVICE_NAME, 
477                               DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
478   if (dbus_error_is_set(&err)) { 
479     g_warning("Requesting service name failed (%s)\n", err.message); 
480     dbus_error_free(&err); 
481   }
482   g_assert(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER == ret);
483
484   dbus_bus_add_match(conn, "type='method_call'", &err);
485   if (dbus_error_is_set(&err)) { 
486     g_warning("Add match failed (%s)\n", err.message); 
487     dbus_error_free(&err);
488     exit(1);
489   }
490
491   loop = g_main_loop_new(NULL, FALSE);
492   dbus_connection_setup_with_g_main(conn, NULL);
493   
494         g_print("Unit ready to accept method calls\n");
495   g_main_loop_run(loop);
496   
497   return 0;
498 }