3a49f07ba6fc6fd80b131200b430b83a231295cf
[platform/upstream/glib.git] / gio / tests / gdbus-serialization.c
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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include <gio/gio.h>
24
25 #include <string.h>
26 #include <unistd.h>
27 #include <dbus/dbus.h>
28
29 /* ---------------------------------------------------------------------------------------------------- */
30
31 static void
32 hexdump (const guchar *str, gsize len)
33 {
34   const guchar *data = (const guchar *) str;
35   guint n, m;
36
37   for (n = 0; n < len; n += 16)
38     {
39       g_printerr ("%04x: ", n);
40
41       for (m = n; m < n + 16; m++)
42         {
43           if (m > n && (m%4) == 0)
44             g_printerr (" ");
45           if (m < len)
46             g_printerr ("%02x ", data[m]);
47           else
48             g_printerr ("   ");
49         }
50
51       g_printerr ("   ");
52
53       for (m = n; m < len && m < n + 16; m++)
54         g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.');
55
56       g_printerr ("\n");
57     }
58 }
59
60 /* ---------------------------------------------------------------------------------------------------- */
61
62 static gboolean
63 append_gv_to_dbus_iter (DBusMessageIter  *iter,
64                         GVariant         *value,
65                         GError          **error)
66 {
67   const GVariantType *type;
68
69   type = g_variant_get_type (value);
70   if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
71     {
72       dbus_bool_t v = g_variant_get_boolean (value);
73       dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v);
74     }
75   else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
76     {
77       guint8 v = g_variant_get_byte (value);
78       dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v);
79     }
80   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
81     {
82       gint16 v = g_variant_get_int16 (value);
83       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v);
84     }
85   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
86     {
87       guint16 v = g_variant_get_uint16 (value);
88       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v);
89     }
90   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
91     {
92       gint32 v = g_variant_get_int32 (value);
93       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v);
94     }
95   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
96     {
97       guint32 v = g_variant_get_uint32 (value);
98       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v);
99     }
100   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
101     {
102       gint64 v = g_variant_get_int64 (value);
103       dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v);
104     }
105   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
106     {
107       guint64 v = g_variant_get_uint64 (value);
108       dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v);
109     }
110   else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
111     {
112       gdouble v = g_variant_get_double (value);
113       dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v);
114     }
115   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
116     {
117       const gchar *v = g_variant_get_string (value, NULL);
118       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v);
119     }
120   else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
121     {
122       const gchar *v = g_variant_get_string (value, NULL);
123       dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v);
124     }
125   else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
126     {
127       const gchar *v = g_variant_get_string (value, NULL);
128       dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v);
129     }
130   else if (g_variant_type_is_variant (type))
131     {
132       DBusMessageIter sub;
133       GVariant *child;
134
135       child = g_variant_get_child_value (value, 0);
136       dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT,
137                                         g_variant_get_type_string (child),
138                                         &sub);
139       if (!append_gv_to_dbus_iter (&sub, child, error))
140         {
141             g_variant_unref (child);
142             goto fail;
143         }
144       dbus_message_iter_close_container (iter, &sub);
145       g_variant_unref (child);
146     }
147   else if (g_variant_type_is_array (type))
148     {
149       DBusMessageIter dbus_iter;
150       const gchar *type_string;
151       GVariantIter gv_iter;
152       GVariant *item;
153
154       type_string = g_variant_get_type_string (value);
155       type_string++; /* skip the 'a' */
156
157       dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
158                                         type_string, &dbus_iter);
159       g_variant_iter_init (&gv_iter, value);
160
161       while ((item = g_variant_iter_next_value (&gv_iter)))
162         {
163           if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
164             {
165               goto fail;
166             }
167         }
168
169       dbus_message_iter_close_container (iter, &dbus_iter);
170     }
171   else if (g_variant_type_is_tuple (type))
172     {
173       DBusMessageIter dbus_iter;
174       GVariantIter gv_iter;
175       GVariant *item;
176
177       dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT,
178                                         NULL, &dbus_iter);
179       g_variant_iter_init (&gv_iter, value);
180
181       while ((item = g_variant_iter_next_value (&gv_iter)))
182         {
183           if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
184             goto fail;
185         }
186
187       dbus_message_iter_close_container (iter, &dbus_iter);
188     }
189   else if (g_variant_type_is_dict_entry (type))
190     {
191       DBusMessageIter dbus_iter;
192       GVariant *key, *val;
193
194       dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY,
195                                         NULL, &dbus_iter);
196       key = g_variant_get_child_value (value, 0);
197       if (!append_gv_to_dbus_iter (&dbus_iter, key, error))
198         {
199           g_variant_unref (key);
200           goto fail;
201         }
202       g_variant_unref (key);
203
204       val = g_variant_get_child_value (value, 1);
205       if (!append_gv_to_dbus_iter (&dbus_iter, val, error))
206         {
207           g_variant_unref (val);
208           goto fail;
209         }
210       g_variant_unref (val);
211
212       dbus_message_iter_close_container (iter, &dbus_iter);
213     }
214   else
215     {
216       g_set_error (error,
217                    G_IO_ERROR,
218                    G_IO_ERROR_INVALID_ARGUMENT,
219                    "Error serializing GVariant with type-string `%s' to a D-Bus message",
220                    g_variant_get_type_string (value));
221       goto fail;
222     }
223
224   return TRUE;
225
226  fail:
227   return FALSE;
228 }
229
230 static gboolean
231 append_gv_to_dbus_message (DBusMessage  *message,
232                            GVariant     *value,
233                            GError      **error)
234 {
235   gboolean ret;
236   guint n;
237
238   ret = FALSE;
239
240   if (value != NULL)
241     {
242       DBusMessageIter iter;
243       GVariantIter gv_iter;
244       GVariant *item;
245
246       dbus_message_iter_init_append (message, &iter);
247
248       g_variant_iter_init (&gv_iter, value);
249       n = 0;
250       while ((item = g_variant_iter_next_value (&gv_iter)))
251         {
252           if (!append_gv_to_dbus_iter (&iter, item, error))
253             {
254               g_prefix_error (error,
255                               "Error encoding in-arg %d: ",
256                               n);
257               goto out;
258             }
259           n++;
260         }
261     }
262
263   ret = TRUE;
264
265  out:
266   return ret;
267 }
268
269 static void
270 print_gv_dbus_message (GVariant *value)
271 {
272   DBusMessage *message;
273   char *blob;
274   int blob_len;
275   GError *error;
276
277   message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
278   dbus_message_set_serial (message, 0x41);
279   dbus_message_set_path (message, "/foo/bar");
280   dbus_message_set_member (message, "Member");
281
282   error = NULL;
283   if (!append_gv_to_dbus_message (message, value, &error))
284     {
285       g_printerr ("Error printing GVariant as DBusMessage: %s", error->message);
286       g_error_free (error);
287       goto out;
288     }
289
290   dbus_message_marshal (message, &blob, &blob_len);
291   g_printerr ("\n");
292   hexdump ((guchar *) blob, blob_len);
293  out:
294   dbus_message_unref (message);
295 }
296
297 /* ---------------------------------------------------------------------------------------------------- */
298
299 static void
300 dbus_1_message_append (GString *s,
301                        guint indent,
302                        DBusMessageIter *iter)
303 {
304   gint arg_type;
305   DBusMessageIter sub;
306
307   g_string_append_printf (s, "%*s", indent, "");
308
309   arg_type = dbus_message_iter_get_arg_type (iter);
310   switch (arg_type)
311     {
312      case DBUS_TYPE_BOOLEAN:
313       {
314         dbus_bool_t value;
315         dbus_message_iter_get_basic (iter, &value);
316         g_string_append_printf (s, "bool: %s\n", value ? "true" : "false");
317         break;
318       }
319
320      case DBUS_TYPE_BYTE:
321       {
322         guchar value;
323         dbus_message_iter_get_basic (iter, &value);
324         g_string_append_printf (s, "byte: 0x%02x\n", (guint) value);
325         break;
326       }
327
328      case DBUS_TYPE_INT16:
329       {
330         gint16 value;
331         dbus_message_iter_get_basic (iter, &value);
332         g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value);
333         break;
334       }
335
336      case DBUS_TYPE_UINT16:
337       {
338         guint16 value;
339         dbus_message_iter_get_basic (iter, &value);
340         g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value);
341         break;
342       }
343
344      case DBUS_TYPE_INT32:
345       {
346         gint32 value;
347         dbus_message_iter_get_basic (iter, &value);
348         g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value);
349         break;
350       }
351
352      case DBUS_TYPE_UINT32:
353       {
354         guint32 value;
355         dbus_message_iter_get_basic (iter, &value);
356         g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value);
357         break;
358       }
359
360      case DBUS_TYPE_INT64:
361       {
362         gint64 value;
363         dbus_message_iter_get_basic (iter, &value);
364         g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value);
365         break;
366       }
367
368      case DBUS_TYPE_UINT64:
369       {
370         guint64 value;
371         dbus_message_iter_get_basic (iter, &value);
372         g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value);
373         break;
374       }
375
376      case DBUS_TYPE_DOUBLE:
377       {
378         gdouble value;
379         dbus_message_iter_get_basic (iter, &value);
380         g_string_append_printf (s, "double: %f\n", value);
381         break;
382       }
383
384      case DBUS_TYPE_STRING:
385       {
386         const gchar *value;
387         dbus_message_iter_get_basic (iter, &value);
388         g_string_append_printf (s, "string: `%s'\n", value);
389         break;
390       }
391
392      case DBUS_TYPE_OBJECT_PATH:
393       {
394         const gchar *value;
395         dbus_message_iter_get_basic (iter, &value);
396         g_string_append_printf (s, "object_path: `%s'\n", value);
397         break;
398       }
399
400      case DBUS_TYPE_SIGNATURE:
401       {
402         const gchar *value;
403         dbus_message_iter_get_basic (iter, &value);
404         g_string_append_printf (s, "signature: `%s'\n", value);
405         break;
406       }
407
408      case DBUS_TYPE_VARIANT:
409        g_string_append_printf (s, "variant:\n");
410        dbus_message_iter_recurse (iter, &sub);
411        while (dbus_message_iter_get_arg_type (&sub))
412          {
413            dbus_1_message_append (s, indent + 2, &sub);
414            dbus_message_iter_next (&sub);
415          }
416        break;
417
418      case DBUS_TYPE_ARRAY:
419        g_string_append_printf (s, "array:\n");
420        dbus_message_iter_recurse (iter, &sub);
421        while (dbus_message_iter_get_arg_type (&sub))
422          {
423            dbus_1_message_append (s, indent + 2, &sub);
424            dbus_message_iter_next (&sub);
425          }
426        break;
427
428      case DBUS_TYPE_STRUCT:
429        g_string_append_printf (s, "struct:\n");
430        dbus_message_iter_recurse (iter, &sub);
431        while (dbus_message_iter_get_arg_type (&sub))
432          {
433            dbus_1_message_append (s, indent + 2, &sub);
434            dbus_message_iter_next (&sub);
435          }
436        break;
437
438      case DBUS_TYPE_DICT_ENTRY:
439        g_string_append_printf (s, "dict_entry:\n");
440        dbus_message_iter_recurse (iter, &sub);
441        while (dbus_message_iter_get_arg_type (&sub))
442          {
443            dbus_1_message_append (s, indent + 2, &sub);
444            dbus_message_iter_next (&sub);
445          }
446        break;
447
448      default:
449        g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)",
450                    arg_type,
451                    arg_type);
452        g_assert_not_reached ();
453        break;
454     }
455 }
456
457 static gchar *
458 dbus_1_message_print (DBusMessage *message)
459 {
460   GString *s;
461   guint n;
462   DBusMessageIter iter;
463
464   s = g_string_new (NULL);
465   n = 0;
466   dbus_message_iter_init (message, &iter);
467   while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
468     {
469       g_string_append_printf (s, "value %d: ", n);
470       dbus_1_message_append (s, 2, &iter);
471       dbus_message_iter_next (&iter);
472       n++;
473     }
474
475   return g_string_free (s, FALSE);
476 }
477
478 /* ---------------------------------------------------------------------------------------------------- */
479
480 static gchar *
481 get_body_signature (GVariant *value)
482 {
483   const gchar *s;
484   gsize len;
485   gchar *ret;
486
487   s = g_variant_get_type_string (value);
488   len = strlen (s);
489   g_assert (len>=2);
490
491   ret = g_strndup (s + 1, len - 2);
492
493   return ret;
494 }
495
496 static void
497 check_serialization (GVariant *value,
498                      const gchar *expected_dbus_1_output)
499 {
500   guchar *blob;
501   gsize blob_size;
502   DBusMessage *dbus_1_message;
503   GDBusMessage *message;
504   GDBusMessage *recovered_message;
505   GError *error;
506   DBusError dbus_error;
507   gchar *s;
508   gchar *s1;
509
510   message = g_dbus_message_new ();
511   g_dbus_message_set_body (message, value);
512   g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
513   g_dbus_message_set_serial (message, 0x41);
514   s = get_body_signature (value);
515   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar"));
516   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member"));
517   g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s));
518   g_free (s);
519
520   /* First check that the serialization to the D-Bus wire format is correct */
521
522   error = NULL;
523   blob = g_dbus_message_to_blob (message,
524                                  &blob_size,
525                                  G_DBUS_CAPABILITY_FLAGS_NONE,
526                                  &error);
527   g_assert_no_error (error);
528   g_assert (blob != NULL);
529
530   dbus_error_init (&dbus_error);
531   dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error);
532   if (dbus_error_is_set (&dbus_error))
533     {
534       g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n",
535                   dbus_error.name,
536                   dbus_error.message);
537       hexdump (blob, blob_size);
538       dbus_error_free (&dbus_error);
539
540       s = g_variant_print (value, TRUE);
541       g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s);
542       g_free (s);
543
544       g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n");
545       print_gv_dbus_message (value);
546
547       g_assert_not_reached ();
548     }
549
550   s = dbus_1_message_print (dbus_1_message);
551   dbus_message_unref (dbus_1_message);
552
553   g_assert_cmpstr (s, ==, expected_dbus_1_output);
554   g_free (s);
555
556   /* Then serialize back and check that the body is identical */
557
558   error = NULL;
559   recovered_message = g_dbus_message_new_from_blob (blob,
560                                                     blob_size,
561                                                     G_DBUS_CAPABILITY_FLAGS_NONE,
562                                                     &error);
563   g_assert_no_error (error);
564   g_assert (recovered_message != NULL);
565   g_assert (g_dbus_message_get_body (recovered_message) != NULL);
566
567   if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value))
568     {
569       s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE);
570       s1 = g_variant_print (value, TRUE);
571       g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n",
572                   s,
573                   s1);
574       g_free (s);
575       g_free (s1);
576       g_assert_not_reached ();
577     }
578   g_object_unref (message);
579   g_object_unref (recovered_message);
580 }
581
582 static void
583 message_serialize_basic (void)
584 {
585   check_serialization (g_variant_new ("(sogybnqiuxtd)",
586                                       "this is a string",
587                                       "/this/is/a/path",
588                                       "sad",
589                                       42,
590                                       TRUE,
591                                       -42,
592                                       60000,
593                                       -44,
594                                       100000,
595                                       -G_GINT64_CONSTANT(2)<<34,
596                                       G_GUINT64_CONSTANT(0xffffffffffffffff),
597                                       42.5),
598                        "value 0:   string: `this is a string'\n"
599                        "value 1:   object_path: `/this/is/a/path'\n"
600                        "value 2:   signature: `sad'\n"
601                        "value 3:   byte: 0x2a\n"
602                        "value 4:   bool: true\n"
603                        "value 5:   int16: -42\n"
604                        "value 6:   uint16: 60000\n"
605                        "value 7:   int32: -44\n"
606                        "value 8:   uint32: 100000\n"
607                        "value 9:   int64: -34359738368\n"
608                        "value 10:   uint64: 18446744073709551615\n"
609                        "value 11:   double: 42.500000\n");
610 }
611
612 /* ---------------------------------------------------------------------------------------------------- */
613
614 static void
615 message_serialize_complex (void)
616 {
617   GError *error;
618   GVariant *value;
619
620   error = NULL;
621
622   value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
623                            "([1, 2, 3], {'one': 'white', 'two': 'black'})",
624                            NULL, NULL, &error);
625   g_assert_no_error (error);
626   g_assert (value != NULL);
627   check_serialization (value,
628                        "value 0:   array:\n"
629                        "    int32: 1\n"
630                        "    int32: 2\n"
631                        "    int32: 3\n"
632                        "value 1:   array:\n"
633                        "    dict_entry:\n"
634                        "      string: `one'\n"
635                        "      string: `white'\n"
636                        "    dict_entry:\n"
637                        "      string: `two'\n"
638                        "      string: `black'\n");
639
640   value = g_variant_parse (G_VARIANT_TYPE ("(sa{sv}as)"),
641                            "('01234567890123456', {}, ['Something'])",
642                            NULL, NULL, &error);
643   g_assert_no_error (error);
644   g_assert (value != NULL);
645   check_serialization (value,
646                        "value 0:   string: `01234567890123456'\n"
647                        "value 1:   array:\n"
648                        "value 2:   array:\n"
649                        "    string: `Something'\n");
650 }
651
652
653 /* ---------------------------------------------------------------------------------------------------- */
654
655 int
656 main (int   argc,
657       char *argv[])
658 {
659   g_type_init ();
660   g_test_init (&argc, &argv, NULL);
661
662   g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic);
663   g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex);
664   return g_test_run();
665 }
666