rename test: gvarianttype -> gvariant
[platform/upstream/glib.git] / glib / tests / gvariant.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * See the included COPYING file for more information.
10  *
11  * Author: Ryan Lortie <desrt@desrt.ca>
12  */
13
14 #include <string.h>
15 #include <glib.h>
16
17 #define BASIC "bynqiuxthdsog?"
18 #define N_BASIC (G_N_ELEMENTS (BASIC) - 1)
19
20 #define INVALIDS "cefjklpwz&@^$"
21 #define N_INVALIDS (G_N_ELEMENTS (INVALIDS) - 1)
22
23 static gboolean
24 randomly (gdouble prob)
25 {
26   return g_test_rand_double_range (0, 1) < prob;
27 }
28
29 /* corecursion */
30 static GVariantType *
31 append_tuple_type_string (GString *, GString *, gboolean, gint);
32
33 /* append a random GVariantType to a GString
34  * append a description of the type to another GString
35  * return what the type is
36  */
37 static GVariantType *
38 append_type_string (GString  *string,
39                     GString  *description,
40                     gboolean  definite,
41                     gint      depth)
42 {
43   if (!depth-- || randomly (0.3))
44     {
45       gchar b = BASIC[g_test_rand_int_range (0, N_BASIC - definite)];
46       g_string_append_c (string, b);
47       g_string_append_c (description, b);
48
49       switch (b)
50         {
51         case 'b':
52           return g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
53         case 'y':
54           return g_variant_type_copy (G_VARIANT_TYPE_BYTE);
55         case 'n':
56           return g_variant_type_copy (G_VARIANT_TYPE_INT16);
57         case 'q':
58           return g_variant_type_copy (G_VARIANT_TYPE_UINT16);
59         case 'i':
60           return g_variant_type_copy (G_VARIANT_TYPE_INT32);
61         case 'u':
62           return g_variant_type_copy (G_VARIANT_TYPE_UINT32);
63         case 'x':
64           return g_variant_type_copy (G_VARIANT_TYPE_INT64);
65         case 't':
66           return g_variant_type_copy (G_VARIANT_TYPE_UINT64);
67         case 'h':
68           return g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
69         case 'd':
70           return g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
71         case 's':
72           return g_variant_type_copy (G_VARIANT_TYPE_STRING);
73         case 'o':
74           return g_variant_type_copy (G_VARIANT_TYPE_OBJECT_PATH);
75         case 'g':
76           return g_variant_type_copy (G_VARIANT_TYPE_SIGNATURE);
77         case '?':
78           return g_variant_type_copy (G_VARIANT_TYPE_BASIC);
79         default:
80           g_assert_not_reached ();
81         }
82     }
83   else
84     {
85       GVariantType *result;
86
87       switch (g_test_rand_int_range (0, definite ? 5 : 7))
88         {
89         case 0:
90           {
91             GVariantType *element;
92
93             g_string_append_c (string, 'a');
94             g_string_append (description, "a of ");
95             element = append_type_string (string, description,
96                                           definite, depth);
97             result = g_variant_type_new_array (element);
98             g_variant_type_free (element);
99           }
100
101           g_assert (g_variant_type_is_array (result));
102           break;
103
104         case 1:
105           {
106             GVariantType *element;
107
108             g_string_append_c (string, 'm');
109             g_string_append (description, "m of ");
110             element = append_type_string (string, description,
111                                           definite, depth);
112             result = g_variant_type_new_maybe (element);
113             g_variant_type_free (element);
114           }
115
116           g_assert (g_variant_type_is_maybe (result));
117           break;
118
119         case 2:
120           result = append_tuple_type_string (string, description,
121                                              definite, depth);
122
123           g_assert (g_variant_type_is_tuple (result));
124           break;
125
126         case 3:
127           {
128             GVariantType *key, *value;
129
130             g_string_append_c (string, '{');
131             g_string_append (description, "e of [");
132             key = append_type_string (string, description, definite, 0);
133             g_string_append (description, ", ");
134             value = append_type_string (string, description, definite, depth);
135             g_string_append_c (description, ']');
136             g_string_append_c (string, '}');
137             result = g_variant_type_new_dict_entry (key, value);
138             g_variant_type_free (key);
139             g_variant_type_free (value);
140           }
141
142           g_assert (g_variant_type_is_dict_entry (result));
143           break;
144
145         case 4:
146           g_string_append_c (string, 'v');
147           g_string_append_c (description, 'V');
148           result = g_variant_type_copy (G_VARIANT_TYPE_VARIANT);
149           g_assert (g_variant_type_equal (result, G_VARIANT_TYPE_VARIANT));
150           break;
151
152         case 5:
153           g_string_append_c (string, '*');
154           g_string_append_c (description, 'S');
155           result = g_variant_type_copy (G_VARIANT_TYPE_ANY);
156           g_assert (g_variant_type_equal (result, G_VARIANT_TYPE_ANY));
157           break;
158
159         case 6:
160           g_string_append_c (string, 'r');
161           g_string_append_c (description, 'R');
162           result = g_variant_type_copy (G_VARIANT_TYPE_TUPLE);
163           g_assert (g_variant_type_is_tuple (result));
164           break;
165
166         default:
167           g_assert_not_reached ();
168         }
169
170       return result;
171     }
172 }
173
174 static GVariantType *
175 append_tuple_type_string (GString  *string,
176                           GString  *description,
177                           gboolean  definite,
178                           gint      depth)
179 {
180   GVariantType *result, *other_result;
181   GVariantType **types;
182   gint size;
183   gint i;
184
185   g_string_append_c (string, '(');
186   g_string_append (description, "t of [");
187
188   size = g_test_rand_int_range (0, 20);
189   types = g_new (GVariantType *, size + 1);
190
191   for (i = 0; i < size; i++)
192     {
193       types[i] = append_type_string (string, description, definite, depth);
194
195       if (i < size - 1)
196         g_string_append (description, ", ");
197     }
198
199   types[i] = NULL;
200
201   g_string_append_c (description, ']');
202   g_string_append_c (string, ')');
203
204   result = g_variant_type_new_tuple ((gpointer) types, size);
205   other_result = g_variant_type_new_tuple ((gpointer) types, -1);
206   g_assert (g_variant_type_equal (result, other_result));
207   g_variant_type_free (other_result);
208   for (i = 0; i < size; i++)
209     g_variant_type_free (types[i]);
210   g_free (types);
211
212   return result;
213 }
214
215 /* given a valid type string, make it invalid */
216 static gchar *
217 invalid_mutation (const gchar *type_string)
218 {
219   gboolean have_parens, have_braces;
220
221   /* it's valid, so '(' implies ')' and same for '{' and '}' */
222   have_parens = strchr (type_string, '(') != NULL;
223   have_braces = strchr (type_string, '{') != NULL;
224
225   if (have_parens && have_braces && randomly (0.3))
226     {
227       /* swap a paren and a brace */
228       gchar *pp, *bp;
229       gint np, nb;
230       gchar p, b;
231       gchar *new;
232
233       new = g_strdup (type_string);
234
235       if (randomly (0.5))
236         p = '(', b = '{';
237       else
238         p = ')', b = '}';
239
240       np = nb = 0;
241       pp = bp = new - 1;
242
243       /* count number of parens/braces */
244       while ((pp = strchr (pp + 1, p))) np++;
245       while ((bp = strchr (bp + 1, b))) nb++;
246
247       /* randomly pick one of each */
248       np = g_test_rand_int_range (0, np) + 1;
249       nb = g_test_rand_int_range (0, nb) + 1;
250
251       /* find it */
252       pp = bp = new - 1;
253       while (np--) pp = strchr (pp + 1, p);
254       while (nb--) bp = strchr (bp + 1, b);
255
256       /* swap */
257       g_assert (*bp == b && *pp == p);
258       *bp = p;
259       *pp = b;
260
261       return new;
262     }
263
264   if ((have_parens || have_braces) && randomly (0.3))
265     {
266       /* drop a paren/brace */
267       gchar *new;
268       gchar *pp;
269       gint np;
270       gchar p;
271
272       if (have_parens)
273         if (randomly (0.5)) p = '('; else p = ')';
274       else
275         if (randomly (0.5)) p = '{'; else p = '}';
276
277       new = g_strdup (type_string);
278
279       np = 0;
280       pp = new - 1;
281       while ((pp = strchr (pp + 1, p))) np++;
282       np = g_test_rand_int_range (0, np) + 1;
283       pp = new - 1;
284       while (np--) pp = strchr (pp + 1, p);
285       g_assert (*pp == p);
286
287       while (*pp)
288         {
289           *pp = *(pp + 1);
290           pp++;
291         }
292
293       return new;
294     }
295
296   /* else, perform a random mutation at a random point */
297   {
298     gint length, n;
299     gchar *new;
300     gchar p;
301
302     if (randomly (0.3))
303       {
304         /* insert a paren/brace */
305         if (randomly (0.5))
306           if (randomly (0.5)) p = '('; else p = ')';
307         else
308           if (randomly (0.5)) p = '{'; else p = '}';
309       }
310     else if (randomly (0.5))
311       {
312         /* insert junk */
313         p = INVALIDS[g_test_rand_int_range (0, N_INVALIDS)];
314       }
315     else
316       {
317         /* truncate */
318         p = '\0';
319       }
320
321
322     length = strlen (type_string);
323     new = g_malloc (length + 2);
324     n = g_test_rand_int_range (0, length);
325     memcpy (new, type_string, n);
326     new[n] = p;
327     memcpy (new + n + 1, type_string + n, length - n);
328     new[length + 1] = '\0';
329
330     return new;
331   }
332 }
333
334 /* describe a type using the same language as is generated
335  * while generating the type with append_type_string
336  */
337 static gchar *
338 describe_type (const GVariantType *type)
339 {
340   gchar *result;
341
342   if (g_variant_type_is_container (type))
343     {
344       g_assert (!g_variant_type_is_basic (type));
345
346       if (g_variant_type_is_array (type))
347         {
348           gchar *subtype = describe_type (g_variant_type_element (type));
349           result = g_strdup_printf ("a of %s", subtype);
350           g_free (subtype);
351         }
352       else if (g_variant_type_is_maybe (type))
353         {
354           gchar *subtype = describe_type (g_variant_type_element (type));
355           result = g_strdup_printf ("m of %s", subtype);
356           g_free (subtype);
357         }
358       else if (g_variant_type_is_tuple (type))
359         {
360           if (!g_variant_type_equal (type, G_VARIANT_TYPE_TUPLE))
361             {
362               const GVariantType *sub;
363               GString *string;
364               gint length;
365               gint i;
366
367               string = g_string_new ("t of [");
368
369               length = g_variant_type_n_items (type);
370               sub = g_variant_type_first (type);
371               for (i = 0; i < length; i++)
372                 {
373                   gchar *subtype = describe_type (sub);
374                   g_string_append (string, subtype);
375                   g_free (subtype);
376
377                   if ((sub = g_variant_type_next (sub)))
378                     g_string_append (string, ", ");
379                 }
380               g_assert (sub == NULL);
381               g_string_append_c (string, ']');
382
383               result = g_string_free (string, FALSE);
384             }
385           else
386             result = g_strdup ("R");
387         }
388       else if (g_variant_type_is_dict_entry (type))
389         {
390           gchar *key, *value, *key2, *value2;
391
392           key = describe_type (g_variant_type_key (type));
393           value = describe_type (g_variant_type_value (type));
394           key2 = describe_type (g_variant_type_first (type));
395           value2 = describe_type (
396             g_variant_type_next (g_variant_type_first (type)));
397           g_assert (g_variant_type_next (g_variant_type_next (
398             g_variant_type_first (type))) == NULL);
399           g_assert_cmpstr (key, ==, key2);
400           g_assert_cmpstr (value, ==, value2);
401           result = g_strjoin ("", "e of [", key, ", ", value, "]", NULL);
402           g_free (key2);
403           g_free (value2);
404           g_free (key);
405           g_free (value);
406         }
407       else if (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT))
408         {
409           result = g_strdup ("V");
410         }
411       else
412         g_assert_not_reached ();
413     }
414   else
415     {
416       if (g_variant_type_is_definite (type))
417         {
418           g_assert (g_variant_type_is_basic (type));
419
420           if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
421             result = g_strdup ("b");
422           else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
423             result = g_strdup ("y");
424           else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
425             result = g_strdup ("n");
426           else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
427             result = g_strdup ("q");
428           else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
429             result = g_strdup ("i");
430           else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
431             result = g_strdup ("u");
432           else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
433             result = g_strdup ("x");
434           else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
435             result = g_strdup ("t");
436           else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
437             result = g_strdup ("h");
438           else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
439             result = g_strdup ("d");
440           else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
441             result = g_strdup ("s");
442           else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
443             result = g_strdup ("o");
444           else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
445             result = g_strdup ("g");
446           else
447             g_assert_not_reached ();
448         }
449       else
450         {
451           if (g_variant_type_equal (type, G_VARIANT_TYPE_ANY))
452             {
453               result = g_strdup ("S");
454             }
455           else if (g_variant_type_equal (type, G_VARIANT_TYPE_BASIC))
456             {
457               result = g_strdup ("?");
458             }
459           else
460             g_assert_not_reached ();
461         }
462     }
463
464   return result;
465 }
466
467 /* given a type string, replace one of the indefinite type characters in
468  * it with a matching type (possibly the same type).
469  */
470 static gchar *
471 generate_subtype (const gchar *type_string)
472 {
473   GVariantType *replacement;
474   GString *result, *junk;
475   gint length, n = 0, l;
476
477   result = g_string_new (NULL);
478   junk = g_string_new (NULL);
479
480   /* count the number of indefinite type characters */
481   for (length = 0; type_string[length]; length++)
482     n += type_string[length] == 'r' ||
483          type_string[length] == '?' ||
484          type_string[length] == '*';
485   /* length now is strlen (type_string) */
486
487   /* pick one at random to replace */
488   n = g_test_rand_int_range (0, n) + 1;
489
490   /* find it */
491   l = -1;
492   while (n--) l += strcspn (type_string + l + 1, "r?*") + 1;
493   g_assert (type_string[l] == 'r' ||
494             type_string[l] == '?' ||
495             type_string[l] == '*');
496
497   /* store up to that point in a GString */
498   g_string_append_len (result, type_string, l);
499
500   /* then store the replacement in the GString */
501   if (type_string[l] == 'r')
502     replacement = append_tuple_type_string (result, junk, FALSE, 3);
503
504   else if (type_string[l] == '?')
505     replacement = append_type_string (result, junk, FALSE, 0);
506
507   else if (type_string[l] == '*')
508     replacement = append_type_string (result, junk, FALSE, 3);
509
510   else
511     g_assert_not_reached ();
512
513   /* ensure the replacement has the proper type */
514   g_assert (g_variant_type_is_subtype_of (replacement,
515                                           (gpointer) &type_string[l]));
516
517   /* store the rest from the original type string */
518   g_string_append (result, type_string + l + 1);
519
520   g_variant_type_free (replacement);
521   g_string_free (junk, TRUE);
522
523   return g_string_free (result, FALSE);
524 }
525
526 struct typestack
527 {
528   const GVariantType *type;
529   struct typestack *parent;
530 };
531
532 /* given an indefinite type string, replace one of the indefinite
533  * characters in it with a matching type and ensure that the result is a
534  * subtype of the original.  repeat.
535  */
536 static void
537 subtype_check (const gchar      *type_string,
538                struct typestack *parent_ts)
539 {
540   struct typestack ts, *node;
541   gchar *subtype;
542   gint depth = 0;
543
544   subtype = generate_subtype (type_string);
545
546   ts.type = G_VARIANT_TYPE (subtype);
547   ts.parent = parent_ts;
548
549   for (node = &ts; node; node = node->parent)
550     {
551       /* this type should be a subtype of each parent type */
552       g_assert (g_variant_type_is_subtype_of (ts.type, node->type));
553
554       /* it should only be a supertype when it is exactly equal */
555       g_assert (g_variant_type_is_subtype_of (node->type, ts.type) ==
556                 g_variant_type_equal (ts.type, node->type));
557
558       depth++;
559     }
560
561   if (!g_variant_type_is_definite (ts.type) && depth < 5)
562     {
563       /* the type is still indefinite and we haven't repeated too many
564        * times.  go once more.
565        */
566
567       subtype_check (subtype, &ts);
568     }
569
570   g_free (subtype);
571 }
572
573 static void
574 test_gvarianttype (void)
575 {
576   gint i;
577
578   for (i = 0; i < 2000; i++)
579     {
580       GString *type_string, *description;
581       GVariantType *type, *other_type;
582       const GVariantType *ctype;
583       gchar *invalid;
584       gchar *desc;
585
586       type_string = g_string_new (NULL);
587       description = g_string_new (NULL);
588
589       /* generate a random type, its type string and a description
590        *
591        * exercises type constructor functions and g_variant_type_copy()
592        */
593       type = append_type_string (type_string, description, FALSE, 6);
594
595       /* convert the type string to a type and ensure that it is equal
596        * to the one produced with the type constructor routines
597        */
598       ctype = G_VARIANT_TYPE (type_string->str);
599       g_assert (g_variant_type_equal (ctype, type));
600       g_assert (g_variant_type_is_subtype_of (ctype, type));
601       g_assert (g_variant_type_is_subtype_of (type, ctype));
602
603       /* check if the type is indefinite */
604       if (!g_variant_type_is_definite (type))
605         {
606           struct typestack ts = { type, NULL };
607
608           /* if it is indefinite, then replace one of the indefinite
609            * characters with a matching type and ensure that the result
610            * is a subtype of the original type.  repeat.
611            */
612           subtype_check (type_string->str, &ts);
613         }
614       else
615         /* ensure that no indefinite characters appear */
616         g_assert (strcspn (type_string->str, "r?*") == type_string->len);
617
618
619       /* describe the type.
620        *
621        * exercises the type iterator interface
622        */
623       desc = describe_type (type);
624
625       /* make sure the description matches */
626       g_assert_cmpstr (desc, ==, description->str);
627       g_free (desc);
628
629       /* make an invalid mutation to the type and make sure the type
630        * validation routines catch it */
631       invalid = invalid_mutation (type_string->str);
632       g_assert (g_variant_type_string_is_valid (type_string->str));
633       g_assert (!g_variant_type_string_is_valid (invalid));
634       g_free (invalid);
635
636       /* concatenate another type to the type string and ensure that
637        * the result is recognised as being invalid
638        */
639       other_type = append_type_string (type_string, description, FALSE, 2);
640
641       g_string_free (description, TRUE);
642       g_string_free (type_string, TRUE);
643       g_variant_type_free (other_type);
644       g_variant_type_free (type);
645     }
646 }
647
648 #undef  G_GNUC_INTERNAL
649 #define G_GNUC_INTERNAL static
650
651 #define DISABLE_VISIBILITY
652 #include <glib/gvarianttypeinfo.c>
653
654 #define ALIGNED(x, y)   (((x + (y - 1)) / y) * y)
655
656 /* do our own calculation of the fixed_size and alignment of a type
657  * using a simple algorithm to make sure the "fancy" one in the
658  * implementation is correct.
659  */
660 static void
661 calculate_type_info (const GVariantType *type,
662                      gsize              *fixed_size,
663                      guint              *alignment)
664 {
665   if (g_variant_type_is_array (type) ||
666       g_variant_type_is_maybe (type))
667     {
668       calculate_type_info (g_variant_type_element (type), NULL, alignment);
669
670       if (fixed_size)
671         *fixed_size = 0;
672     }
673   else if (g_variant_type_is_tuple (type) ||
674            g_variant_type_is_dict_entry (type))
675     {
676       if (g_variant_type_n_items (type))
677         {
678           const GVariantType *sub;
679           gboolean variable;
680           gsize size;
681           guint al;
682
683           variable = FALSE;
684           size = 0;
685           al = 0;
686
687           sub = g_variant_type_first (type);
688           do
689             {
690               gsize this_fs;
691               guint this_al;
692
693               calculate_type_info (sub, &this_fs, &this_al);
694
695               al = MAX (al, this_al);
696
697               if (!this_fs)
698                 {
699                   variable = TRUE;
700                   size = 0;
701                 }
702
703               if (!variable)
704                 {
705                   size = ALIGNED (size, this_al);
706                   size += this_fs;
707                 }
708             }
709           while ((sub = g_variant_type_next (sub)));
710
711           size = ALIGNED (size, al);
712
713           if (alignment)
714             *alignment = al;
715
716           if (fixed_size)
717             *fixed_size = size;
718         }
719       else
720         {
721           if (fixed_size)
722             *fixed_size = 1;
723
724           if (alignment)
725             *alignment = 1;
726         }
727     }
728   else
729     {
730       gint fs, al;
731
732       if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN) ||
733           g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
734         {
735           al = fs = 1;
736         }
737
738       else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16) ||
739                g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
740         {
741           al = fs = 2;
742         }
743
744       else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32) ||
745                g_variant_type_equal (type, G_VARIANT_TYPE_UINT32) ||
746                g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
747         {
748           al = fs = 4;
749         }
750
751       else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64) ||
752                g_variant_type_equal (type, G_VARIANT_TYPE_UINT64) ||
753                g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
754         {
755           al = fs = 8;
756         }
757       else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING) ||
758                g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH) ||
759                g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
760         {
761           al = 1;
762           fs = 0;
763         }
764       else if (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT))
765         {
766           al = 8;
767           fs = 0;
768         }
769       else
770         g_assert_not_reached ();
771
772       if (fixed_size)
773         *fixed_size = fs;
774
775       if (alignment)
776         *alignment = al;
777     }
778 }
779
780 /* same as the describe_type() function above, but iterates over
781  * typeinfo instead of types.
782  */
783 static gchar *
784 describe_info (GVariantTypeInfo *info)
785 {
786   gchar *result;
787
788   switch (g_variant_type_info_get_type_char (info))
789     {
790     case G_VARIANT_TYPE_INFO_CHAR_MAYBE:
791       {
792         gchar *element;
793
794         element = describe_info (g_variant_type_info_element (info));
795         result = g_strdup_printf ("m of %s", element);
796         g_free (element);
797       }
798       break;
799
800     case G_VARIANT_TYPE_INFO_CHAR_ARRAY:
801       {
802         gchar *element;
803
804         element = describe_info (g_variant_type_info_element (info));
805         result = g_strdup_printf ("a of %s", element);
806         g_free (element);
807       }
808       break;
809
810     case G_VARIANT_TYPE_INFO_CHAR_TUPLE:
811       {
812         const gchar *sep = "";
813         GString *string;
814         gint length;
815         gint i;
816
817         string = g_string_new ("t of [");
818         length = g_variant_type_info_n_members (info);
819
820         for (i = 0; i < length; i++)
821           {
822             const GVariantMemberInfo *minfo;
823             gchar *subtype;
824
825             g_string_append (string, sep);
826             sep = ", ";
827
828             minfo = g_variant_type_info_member_info (info, i);
829             subtype = describe_info (minfo->type_info);
830             g_string_append (string, subtype);
831             g_free (subtype);
832           }
833
834         g_string_append_c (string, ']');
835
836         result = g_string_free (string, FALSE);
837       }
838       break;
839
840     case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY:
841       {
842         const GVariantMemberInfo *keyinfo, *valueinfo;
843         gchar *key, *value;
844
845         g_assert_cmpint (g_variant_type_info_n_members (info), ==, 2);
846         keyinfo = g_variant_type_info_member_info (info, 0);
847         valueinfo = g_variant_type_info_member_info (info, 1);
848         key = describe_info (keyinfo->type_info);
849         value = describe_info (valueinfo->type_info);
850         result = g_strjoin ("", "e of [", key, ", ", value, "]", NULL);
851         g_free (key);
852         g_free (value);
853       }
854       break;
855
856     case G_VARIANT_TYPE_INFO_CHAR_VARIANT:
857       result = g_strdup ("V");
858       break;
859
860     default:
861       result = g_strdup (g_variant_type_info_get_type_string (info));
862       g_assert_cmpint (strlen (result), ==, 1);
863       break;
864     }
865
866   return result;
867 }
868
869 /* check that the O(1) method of calculating offsets meshes with the
870  * results of simple iteration.
871  */
872 static void
873 check_offsets (GVariantTypeInfo   *info,
874                const GVariantType *type)
875 {
876   gint flavour;
877   gint length;
878
879   length = g_variant_type_info_n_members (info);
880   g_assert_cmpint (length, ==, g_variant_type_n_items (type));
881
882   /* the 'flavour' is the low order bits of the ending point of
883    * variable-size items in the tuple.  this lets us test that the type
884    * info is correct for various starting alignments.
885    */
886   for (flavour = 0; flavour < 8; flavour++)
887     {
888       const GVariantType *subtype;
889       gsize last_offset_index;
890       gsize last_offset;
891       gsize position;
892       gint i;
893
894       subtype = g_variant_type_first (type);
895       last_offset_index = -1;
896       last_offset = 0;
897       position = 0;
898
899       /* go through the tuple, keeping track of our position */
900       for (i = 0; i < length; i++)
901         {
902           gsize fixed_size;
903           guint alignment;
904
905           calculate_type_info (subtype, &fixed_size, &alignment);
906
907           position = ALIGNED (position, alignment);
908
909           /* compare our current aligned position (ie: the start of this
910            * item) to the start offset that would be calculated if we
911            * used the type info
912            */
913           {
914             const GVariantMemberInfo *member;
915             gsize start;
916
917             member = g_variant_type_info_member_info (info, i);
918             g_assert_cmpint (member->i, ==, last_offset_index);
919
920             /* do the calculation using the typeinfo */
921             start = last_offset;
922             start += member->a;
923             start &= member->b;
924             start |= member->c;
925
926             /* did we reach the same spot? */
927             g_assert_cmpint (start, ==, position);
928           }
929
930           if (fixed_size)
931             {
932               /* fixed size.  add that size. */
933               position += fixed_size;
934             }
935           else
936             {
937               /* variable size.  do the flavouring. */
938               while ((position & 0x7) != flavour)
939                 position++;
940
941               /* and store the offset, just like it would be in the
942                * serialised data.
943                */
944               last_offset = position;
945               last_offset_index++;
946             }
947
948           /* next type */
949           subtype = g_variant_type_next (subtype);
950         }
951
952       /* make sure we used up exactly all the types */
953       g_assert (subtype == NULL);
954     }
955 }
956
957 static void
958 test_gvarianttypeinfo (void)
959 {
960   gint i;
961
962   for (i = 0; i < 2000; i++)
963     {
964       GString *type_string, *description;
965       gsize fixed_size1, fixed_size2;
966       guint alignment1, alignment2;
967       GVariantTypeInfo *info;
968       GVariantType *type;
969       gchar *desc;
970
971       type_string = g_string_new (NULL);
972       description = g_string_new (NULL);
973
974       /* random type */
975       type = append_type_string (type_string, description, TRUE, 6);
976
977       /* create a typeinfo for it */
978       info = g_variant_type_info_get (type);
979
980       /* make sure the typeinfo has the right type string */
981       g_assert_cmpstr (g_variant_type_info_get_type_string (info), ==,
982                        type_string->str);
983
984       /* calculate the alignment and fixed size, compare to the
985        * typeinfo's calculations
986        */
987       calculate_type_info (type, &fixed_size1, &alignment1);
988       g_variant_type_info_query (info, &alignment2, &fixed_size2);
989       g_assert_cmpint (fixed_size1, ==, fixed_size2);
990       g_assert_cmpint (alignment1, ==, alignment2 + 1);
991
992       /* test the iteration functions over typeinfo structures by
993        * "describing" the typeinfo and verifying equality.
994        */
995       desc = describe_info (info);
996       g_assert_cmpstr (desc, ==, description->str);
997
998       /* do extra checks for containers */
999       if (g_variant_type_is_array (type) ||
1000           g_variant_type_is_maybe (type))
1001         {
1002           const GVariantType *element;
1003           gsize efs1, efs2;
1004           guint ea1, ea2;
1005
1006           element = g_variant_type_element (type);
1007           calculate_type_info (element, &efs1, &ea1);
1008           g_variant_type_info_query_element (info, &ea2, &efs2);
1009           g_assert_cmpint (efs1, ==, efs2);
1010           g_assert_cmpint (ea1, ==, ea2 + 1);
1011
1012           g_assert_cmpint (ea1, ==, alignment1);
1013           g_assert_cmpint (0, ==, fixed_size1);
1014         }
1015       else if (g_variant_type_is_tuple (type) ||
1016                g_variant_type_is_dict_entry (type))
1017         {
1018           /* make sure the "magic constants" are working */
1019           check_offsets (info, type);
1020         }
1021
1022       g_string_free (type_string, TRUE);
1023       g_string_free (description, TRUE);
1024       g_variant_type_info_unref (info);
1025       g_variant_type_free (type);
1026       g_free (desc);
1027     }
1028
1029   assert_no_type_infos ();
1030 }
1031
1032 int
1033 main (int argc, char **argv)
1034 {
1035   g_test_init (&argc, &argv, NULL);
1036
1037   g_test_add_func ("/gvariant/type", test_gvarianttype);
1038   g_test_add_func ("/gvariant/typeinfo", test_gvarianttypeinfo);
1039
1040   return g_test_run ();
1041 }