qapi: move inclusions of qemu-common.h from headers to .c files
[sdk/emulator/qemu.git] / tests / test-visitor-serialization.c
1 /*
2  * Unit-tests for visitor-based serialization
3  *
4  * Copyright IBM, Corp. 2012
5  *
6  * Authors:
7  *  Michael Roth <mdroth@linux.vnet.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12
13 #include <glib.h>
14 #include <stdlib.h>
15 #include <stdint.h>
16 #include <float.h>
17
18 #include "qemu-common.h"
19 #include "test-qapi-types.h"
20 #include "test-qapi-visit.h"
21 #include "qemu-objects.h"
22 #include "qapi/qmp-input-visitor.h"
23 #include "qapi/qmp-output-visitor.h"
24 #include "qapi/string-input-visitor.h"
25 #include "qapi/string-output-visitor.h"
26
27 typedef struct PrimitiveType {
28     union {
29         const char *string;
30         bool boolean;
31         double number;
32         int64_t integer;
33         uint8_t u8;
34         uint16_t u16;
35         uint32_t u32;
36         uint64_t u64;
37         int8_t s8;
38         int16_t s16;
39         int32_t s32;
40         int64_t s64;
41         intmax_t max;
42     } value;
43     enum {
44         PTYPE_STRING = 0,
45         PTYPE_BOOLEAN,
46         PTYPE_NUMBER,
47         PTYPE_INTEGER,
48         PTYPE_U8,
49         PTYPE_U16,
50         PTYPE_U32,
51         PTYPE_U64,
52         PTYPE_S8,
53         PTYPE_S16,
54         PTYPE_S32,
55         PTYPE_S64,
56         PTYPE_EOL,
57     } type;
58     const char *description;
59 } PrimitiveType;
60
61 /* test helpers */
62
63 static void visit_primitive_type(Visitor *v, void **native, Error **errp)
64 {
65     PrimitiveType *pt = *native;
66     switch(pt->type) {
67     case PTYPE_STRING:
68         visit_type_str(v, (char **)&pt->value.string, NULL, errp);
69         break;
70     case PTYPE_BOOLEAN:
71         visit_type_bool(v, &pt->value.boolean, NULL, errp);
72         break;
73     case PTYPE_NUMBER:
74         visit_type_number(v, &pt->value.number, NULL, errp);
75         break;
76     case PTYPE_INTEGER:
77         visit_type_int(v, &pt->value.integer, NULL, errp);
78         break;
79     case PTYPE_U8:
80         visit_type_uint8(v, &pt->value.u8, NULL, errp);
81         break;
82     case PTYPE_U16:
83         visit_type_uint16(v, &pt->value.u16, NULL, errp);
84         break;
85     case PTYPE_U32:
86         visit_type_uint32(v, &pt->value.u32, NULL, errp);
87         break;
88     case PTYPE_U64:
89         visit_type_uint64(v, &pt->value.u64, NULL, errp);
90         break;
91     case PTYPE_S8:
92         visit_type_int8(v, &pt->value.s8, NULL, errp);
93         break;
94     case PTYPE_S16:
95         visit_type_int16(v, &pt->value.s16, NULL, errp);
96         break;
97     case PTYPE_S32:
98         visit_type_int32(v, &pt->value.s32, NULL, errp);
99         break;
100     case PTYPE_S64:
101         visit_type_int64(v, &pt->value.s64, NULL, errp);
102         break;
103     case PTYPE_EOL:
104         g_assert(false);
105     }
106 }
107
108 typedef struct TestStruct
109 {
110     int64_t integer;
111     bool boolean;
112     char *string;
113 } TestStruct;
114
115 static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
116                                   const char *name, Error **errp)
117 {
118     visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), errp);
119
120     visit_type_int(v, &(*obj)->integer, "integer", errp);
121     visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
122     visit_type_str(v, &(*obj)->string, "string", errp);
123
124     visit_end_struct(v, errp);
125 }
126
127 static TestStruct *struct_create(void)
128 {
129     TestStruct *ts = g_malloc0(sizeof(*ts));
130     ts->integer = -42;
131     ts->boolean = true;
132     ts->string = strdup("test string");
133     return ts;
134 }
135
136 static void struct_compare(TestStruct *ts1, TestStruct *ts2)
137 {
138     g_assert(ts1);
139     g_assert(ts2);
140     g_assert_cmpint(ts1->integer, ==, ts2->integer);
141     g_assert(ts1->boolean == ts2->boolean);
142     g_assert_cmpstr(ts1->string, ==, ts2->string);
143 }
144
145 static void struct_cleanup(TestStruct *ts)
146 {
147     g_free(ts->string);
148     g_free(ts);
149 }
150
151 static void visit_struct(Visitor *v, void **native, Error **errp)
152 {
153     visit_type_TestStruct(v, (TestStruct **)native, NULL, errp);
154 }
155
156 static UserDefNested *nested_struct_create(void)
157 {
158     UserDefNested *udnp = g_malloc0(sizeof(*udnp));
159     udnp->string0 = strdup("test_string0");
160     udnp->dict1.string1 = strdup("test_string1");
161     udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
162     udnp->dict1.dict2.userdef1->integer = 42;
163     udnp->dict1.dict2.userdef1->string = strdup("test_string");
164     udnp->dict1.dict2.string2 = strdup("test_string2");
165     udnp->dict1.has_dict3 = true;
166     udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
167     udnp->dict1.dict3.userdef2->integer = 43;
168     udnp->dict1.dict3.userdef2->string = strdup("test_string");
169     udnp->dict1.dict3.string3 = strdup("test_string3");
170     return udnp;
171 }
172
173 static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2)
174 {
175     g_assert(udnp1);
176     g_assert(udnp2);
177     g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
178     g_assert_cmpstr(udnp1->dict1.string1, ==, udnp2->dict1.string1);
179     g_assert_cmpint(udnp1->dict1.dict2.userdef1->integer, ==,
180                     udnp2->dict1.dict2.userdef1->integer);
181     g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==,
182                     udnp2->dict1.dict2.userdef1->string);
183     g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2);
184     g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3);
185     g_assert_cmpint(udnp1->dict1.dict3.userdef2->integer, ==,
186                     udnp2->dict1.dict3.userdef2->integer);
187     g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==,
188                     udnp2->dict1.dict3.userdef2->string);
189     g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3);
190 }
191
192 static void nested_struct_cleanup(UserDefNested *udnp)
193 {
194     qapi_free_UserDefNested(udnp);
195 }
196
197 static void visit_nested_struct(Visitor *v, void **native, Error **errp)
198 {
199     visit_type_UserDefNested(v, (UserDefNested **)native, NULL, errp);
200 }
201
202 static void visit_nested_struct_list(Visitor *v, void **native, Error **errp)
203 {
204     visit_type_UserDefNestedList(v, (UserDefNestedList **)native, NULL, errp);
205 }
206
207 /* test cases */
208
209 typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp);
210
211 typedef enum VisitorCapabilities {
212     VCAP_PRIMITIVES = 1,
213     VCAP_STRUCTURES = 2,
214     VCAP_LISTS = 4,
215 } VisitorCapabilities;
216
217 typedef struct SerializeOps {
218     void (*serialize)(void *native_in, void **datap,
219                       VisitorFunc visit, Error **errp);
220     void (*deserialize)(void **native_out, void *datap,
221                             VisitorFunc visit, Error **errp);
222     void (*cleanup)(void *datap);
223     const char *type;
224     VisitorCapabilities caps;
225 } SerializeOps;
226
227 typedef struct TestArgs {
228     const SerializeOps *ops;
229     void *test_data;
230 } TestArgs;
231
232 #define FLOAT_STRING_PRECISION 6 /* corresponding to n in %.nf formatting */
233 static gsize calc_float_string_storage(double value)
234 {
235     int whole_value = value;
236     gsize i = 0;
237     do {
238         i++;
239     } while (whole_value /= 10);
240     return i + 2 + FLOAT_STRING_PRECISION;
241 }
242
243 static void test_primitives(gconstpointer opaque)
244 {
245     TestArgs *args = (TestArgs *) opaque;
246     const SerializeOps *ops = args->ops;
247     PrimitiveType *pt = args->test_data;
248     PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy));
249     Error *err = NULL;
250     void *serialize_data;
251     char *double1, *double2;
252
253     pt_copy->type = pt->type;
254     ops->serialize(pt, &serialize_data, visit_primitive_type, &err);
255     ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, &err);
256
257     g_assert(err == NULL);
258     g_assert(pt_copy != NULL);
259     if (pt->type == PTYPE_STRING) {
260         g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string);
261     } else if (pt->type == PTYPE_NUMBER) {
262         /* we serialize with %f for our reference visitors, so rather than fuzzy
263          * floating math to test "equality", just compare the formatted values
264          */
265         double1 = g_malloc0(calc_float_string_storage(pt->value.number));
266         double2 = g_malloc0(calc_float_string_storage(pt_copy->value.number));
267         g_assert_cmpstr(double1, ==, double2);
268         g_free(double1);
269         g_free(double2);
270     } else if (pt->type == PTYPE_BOOLEAN) {
271         g_assert_cmpint(!!pt->value.max, ==, !!pt->value.max);
272     } else {
273         g_assert_cmpint(pt->value.max, ==, pt_copy->value.max);
274     }
275
276     ops->cleanup(serialize_data);
277     g_free(args);
278 }
279
280 static void test_struct(gconstpointer opaque)
281 {
282     TestArgs *args = (TestArgs *) opaque;
283     const SerializeOps *ops = args->ops;
284     TestStruct *ts = struct_create();
285     TestStruct *ts_copy = NULL;
286     Error *err = NULL;
287     void *serialize_data;
288
289     ops->serialize(ts, &serialize_data, visit_struct, &err);
290     ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, &err); 
291
292     g_assert(err == NULL);
293     struct_compare(ts, ts_copy);
294
295     struct_cleanup(ts);
296     struct_cleanup(ts_copy);
297
298     ops->cleanup(serialize_data);
299     g_free(args);
300 }
301
302 static void test_nested_struct(gconstpointer opaque)
303 {
304     TestArgs *args = (TestArgs *) opaque;
305     const SerializeOps *ops = args->ops;
306     UserDefNested *udnp = nested_struct_create();
307     UserDefNested *udnp_copy = NULL;
308     Error *err = NULL;
309     void *serialize_data;
310     
311     ops->serialize(udnp, &serialize_data, visit_nested_struct, &err);
312     ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, &err); 
313
314     g_assert(err == NULL);
315     nested_struct_compare(udnp, udnp_copy);
316
317     nested_struct_cleanup(udnp);
318     nested_struct_cleanup(udnp_copy);
319
320     ops->cleanup(serialize_data);
321     g_free(args);
322 }
323
324 static void test_nested_struct_list(gconstpointer opaque)
325 {
326     TestArgs *args = (TestArgs *) opaque;
327     const SerializeOps *ops = args->ops;
328     UserDefNestedList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
329     Error *err = NULL;
330     void *serialize_data;
331     int i = 0;
332
333     for (i = 0; i < 8; i++) {
334         tmp = g_malloc0(sizeof(UserDefNestedList));
335         tmp->value = nested_struct_create();
336         tmp->next = listp;
337         listp = tmp;
338     }
339     
340     ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
341     ops->deserialize((void **)&listp_copy, serialize_data,
342                      visit_nested_struct_list, &err); 
343
344     g_assert(err == NULL);
345
346     tmp = listp;
347     tmp_copy = listp_copy;
348     while (listp_copy) {
349         g_assert(listp);
350         nested_struct_compare(listp->value, listp_copy->value);
351         listp = listp->next;
352         listp_copy = listp_copy->next;
353     }
354
355     qapi_free_UserDefNestedList(tmp);
356     qapi_free_UserDefNestedList(tmp_copy);
357
358     ops->cleanup(serialize_data);
359     g_free(args);
360 }
361
362 PrimitiveType pt_values[] = {
363     /* string tests */
364     {
365         .description = "string_empty",
366         .type = PTYPE_STRING,
367         .value.string = "",
368     },
369     {
370         .description = "string_whitespace",
371         .type = PTYPE_STRING,
372         .value.string = "a b  c\td",
373     },
374     {
375         .description = "string_newlines",
376         .type = PTYPE_STRING,
377         .value.string = "a\nb\n",
378     },
379     {
380         .description = "string_commas",
381         .type = PTYPE_STRING,
382         .value.string = "a,b, c,d",
383     },
384     {
385         .description = "string_single_quoted",
386         .type = PTYPE_STRING,
387         .value.string = "'a b',cd",
388     },
389     {
390         .description = "string_double_quoted",
391         .type = PTYPE_STRING,
392         .value.string = "\"a b\",cd",
393     },
394     /* boolean tests */
395     {
396         .description = "boolean_true1",
397         .type = PTYPE_BOOLEAN,
398         .value.boolean = true,
399     },
400     {
401         .description = "boolean_true2",
402         .type = PTYPE_BOOLEAN,
403         .value.boolean = 8,
404     },
405     {
406         .description = "boolean_true3",
407         .type = PTYPE_BOOLEAN,
408         .value.boolean = -1,
409     },
410     {
411         .description = "boolean_false1",
412         .type = PTYPE_BOOLEAN,
413         .value.boolean = false,
414     },
415     {
416         .description = "boolean_false2",
417         .type = PTYPE_BOOLEAN,
418         .value.boolean = 0,
419     },
420     /* number tests (double) */
421     /* note: we format these to %.6f before comparing, since that's how
422      * we serialize them and it doesn't make sense to check precision
423      * beyond that.
424      */
425     {
426         .description = "number_sanity1",
427         .type = PTYPE_NUMBER,
428         .value.number = -1,
429     },
430     {
431         .description = "number_sanity2",
432         .type = PTYPE_NUMBER,
433         .value.number = 3.14159265,
434     },
435     {
436         .description = "number_min",
437         .type = PTYPE_NUMBER,
438         .value.number = DBL_MIN,
439     },
440     {
441         .description = "number_max",
442         .type = PTYPE_NUMBER,
443         .value.number = DBL_MAX,
444     },
445     /* integer tests (int64) */
446     {
447         .description = "integer_sanity1",
448         .type = PTYPE_INTEGER,
449         .value.integer = -1,
450     },
451     {
452         .description = "integer_sanity2",
453         .type = PTYPE_INTEGER,
454         .value.integer = INT64_MAX / 2 + 1,
455     },
456     {
457         .description = "integer_min",
458         .type = PTYPE_INTEGER,
459         .value.integer = INT64_MIN,
460     },
461     {
462         .description = "integer_max",
463         .type = PTYPE_INTEGER,
464         .value.integer = INT64_MAX,
465     },
466     /* uint8 tests */
467     {
468         .description = "uint8_sanity1",
469         .type = PTYPE_U8,
470         .value.u8 = 1,
471     },
472     {
473         .description = "uint8_sanity2",
474         .type = PTYPE_U8,
475         .value.u8 = UINT8_MAX / 2 + 1,
476     },
477     {
478         .description = "uint8_min",
479         .type = PTYPE_U8,
480         .value.u8 = 0,
481     },
482     {
483         .description = "uint8_max",
484         .type = PTYPE_U8,
485         .value.u8 = UINT8_MAX,
486     },
487     /* uint16 tests */
488     {
489         .description = "uint16_sanity1",
490         .type = PTYPE_U16,
491         .value.u16 = 1,
492     },
493     {
494         .description = "uint16_sanity2",
495         .type = PTYPE_U16,
496         .value.u16 = UINT16_MAX / 2 + 1,
497     },
498     {
499         .description = "uint16_min",
500         .type = PTYPE_U16,
501         .value.u16 = 0,
502     },
503     {
504         .description = "uint16_max",
505         .type = PTYPE_U16,
506         .value.u16 = UINT16_MAX,
507     },
508     /* uint32 tests */
509     {
510         .description = "uint32_sanity1",
511         .type = PTYPE_U32,
512         .value.u32 = 1,
513     },
514     {
515         .description = "uint32_sanity2",
516         .type = PTYPE_U32,
517         .value.u32 = UINT32_MAX / 2 + 1,
518     },
519     {
520         .description = "uint32_min",
521         .type = PTYPE_U32,
522         .value.u32 = 0,
523     },
524     {
525         .description = "uint32_max",
526         .type = PTYPE_U32,
527         .value.u32 = UINT32_MAX,
528     },
529     /* uint64 tests */
530     {
531         .description = "uint64_sanity1",
532         .type = PTYPE_U64,
533         .value.u64 = 1,
534     },
535     {
536         .description = "uint64_sanity2",
537         .type = PTYPE_U64,
538         .value.u64 = UINT64_MAX / 2 + 1,
539     },
540     {
541         .description = "uint64_min",
542         .type = PTYPE_U64,
543         .value.u64 = 0,
544     },
545     {
546         .description = "uint64_max",
547         .type = PTYPE_U64,
548         .value.u64 = UINT64_MAX,
549     },
550     /* int8 tests */
551     {
552         .description = "int8_sanity1",
553         .type = PTYPE_S8,
554         .value.s8 = -1,
555     },
556     {
557         .description = "int8_sanity2",
558         .type = PTYPE_S8,
559         .value.s8 = INT8_MAX / 2 + 1,
560     },
561     {
562         .description = "int8_min",
563         .type = PTYPE_S8,
564         .value.s8 = INT8_MIN,
565     },
566     {
567         .description = "int8_max",
568         .type = PTYPE_S8,
569         .value.s8 = INT8_MAX,
570     },
571     /* int16 tests */
572     {
573         .description = "int16_sanity1",
574         .type = PTYPE_S16,
575         .value.s16 = -1,
576     },
577     {
578         .description = "int16_sanity2",
579         .type = PTYPE_S16,
580         .value.s16 = INT16_MAX / 2 + 1,
581     },
582     {
583         .description = "int16_min",
584         .type = PTYPE_S16,
585         .value.s16 = INT16_MIN,
586     },
587     {
588         .description = "int16_max",
589         .type = PTYPE_S16,
590         .value.s16 = INT16_MAX,
591     },
592     /* int32 tests */
593     {
594         .description = "int32_sanity1",
595         .type = PTYPE_S32,
596         .value.s32 = -1,
597     },
598     {
599         .description = "int32_sanity2",
600         .type = PTYPE_S32,
601         .value.s32 = INT32_MAX / 2 + 1,
602     },
603     {
604         .description = "int32_min",
605         .type = PTYPE_S32,
606         .value.s32 = INT32_MIN,
607     },
608     {
609         .description = "int32_max",
610         .type = PTYPE_S32,
611         .value.s32 = INT32_MAX,
612     },
613     /* int64 tests */
614     {
615         .description = "int64_sanity1",
616         .type = PTYPE_S64,
617         .value.s64 = -1,
618     },
619     {
620         .description = "int64_sanity2",
621         .type = PTYPE_S64,
622         .value.s64 = INT64_MAX / 2 + 1,
623     },
624     {
625         .description = "int64_min",
626         .type = PTYPE_S64,
627         .value.s64 = INT64_MIN,
628     },
629     {
630         .description = "int64_max",
631         .type = PTYPE_S64,
632         .value.s64 = INT64_MAX,
633     },
634     { .type = PTYPE_EOL }
635 };
636
637 /* visitor-specific op implementations */
638
639 typedef struct QmpSerializeData {
640     QmpOutputVisitor *qov;
641     QmpInputVisitor *qiv;
642 } QmpSerializeData;
643
644 static void qmp_serialize(void *native_in, void **datap,
645                           VisitorFunc visit, Error **errp)
646 {
647     QmpSerializeData *d = g_malloc0(sizeof(*d));
648
649     d->qov = qmp_output_visitor_new();
650     visit(qmp_output_get_visitor(d->qov), &native_in, errp);
651     *datap = d;
652 }
653
654 static void qmp_deserialize(void **native_out, void *datap,
655                             VisitorFunc visit, Error **errp)
656 {
657     QmpSerializeData *d = datap;
658     QString *output_json = qobject_to_json(qmp_output_get_qobject(d->qov));
659     QObject *obj = qobject_from_json(qstring_get_str(output_json));
660
661     QDECREF(output_json);
662     d->qiv = qmp_input_visitor_new(obj);
663     visit(qmp_input_get_visitor(d->qiv), native_out, errp);
664 }
665
666 static void qmp_cleanup(void *datap)
667 {
668     QmpSerializeData *d = datap;
669     qmp_output_visitor_cleanup(d->qov);
670     qmp_input_visitor_cleanup(d->qiv);
671 }
672
673 typedef struct StringSerializeData {
674     StringOutputVisitor *sov;
675     StringInputVisitor *siv;
676 } StringSerializeData;
677
678 static void string_serialize(void *native_in, void **datap,
679                              VisitorFunc visit, Error **errp)
680 {
681     StringSerializeData *d = g_malloc0(sizeof(*d));
682
683     d->sov = string_output_visitor_new();
684     visit(string_output_get_visitor(d->sov), &native_in, errp);
685     *datap = d;
686 }
687
688 static void string_deserialize(void **native_out, void *datap,
689                                VisitorFunc visit, Error **errp)
690 {
691     StringSerializeData *d = datap;
692
693     d->siv = string_input_visitor_new(string_output_get_string(d->sov));
694     visit(string_input_get_visitor(d->siv), native_out, errp);
695 }
696
697 static void string_cleanup(void *datap)
698 {
699     StringSerializeData *d = datap;
700     string_output_visitor_cleanup(d->sov);
701     string_input_visitor_cleanup(d->siv);
702 }
703
704 /* visitor registration, test harness */
705
706 /* note: to function interchangeably as a serialization mechanism your
707  * visitor test implementation should pass the test cases for all visitor
708  * capabilities: primitives, structures, and lists
709  */
710 static const SerializeOps visitors[] = {
711     {
712         .type = "QMP",
713         .serialize = qmp_serialize,
714         .deserialize = qmp_deserialize,
715         .cleanup = qmp_cleanup,
716         .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS
717     },
718     {
719         .type = "String",
720         .serialize = string_serialize,
721         .deserialize = string_deserialize,
722         .cleanup = string_cleanup,
723         .caps = VCAP_PRIMITIVES
724     },
725     { NULL }
726 };
727
728 static void add_visitor_type(const SerializeOps *ops)
729 {
730     char testname_prefix[128];
731     char testname[128];
732     TestArgs *args;
733     int i = 0;
734
735     sprintf(testname_prefix, "/visitor/serialization/%s", ops->type);
736
737     if (ops->caps & VCAP_PRIMITIVES) {
738         while (pt_values[i].type != PTYPE_EOL) {
739             sprintf(testname, "%s/primitives/%s", testname_prefix,
740                     pt_values[i].description);
741             args = g_malloc0(sizeof(*args));
742             args->ops = ops;
743             args->test_data = &pt_values[i];
744             g_test_add_data_func(testname, args, test_primitives);
745             i++;
746         }
747     }
748
749     if (ops->caps & VCAP_STRUCTURES) {
750         sprintf(testname, "%s/struct", testname_prefix);
751         args = g_malloc0(sizeof(*args));
752         args->ops = ops;
753         args->test_data = NULL;
754         g_test_add_data_func(testname, args, test_struct);
755
756         sprintf(testname, "%s/nested_struct", testname_prefix);
757         args = g_malloc0(sizeof(*args));
758         args->ops = ops;
759         args->test_data = NULL;
760         g_test_add_data_func(testname, args, test_nested_struct);
761     }
762
763     if (ops->caps & VCAP_LISTS) {
764         sprintf(testname, "%s/nested_struct_list", testname_prefix);
765         args = g_malloc0(sizeof(*args));
766         args->ops = ops;
767         args->test_data = NULL;
768         g_test_add_data_func(testname, args, test_nested_struct_list);
769     }
770 }
771
772 int main(int argc, char **argv)
773 {
774     int i = 0;
775
776     g_test_init(&argc, &argv, NULL);
777
778     while (visitors[i].type != NULL) {
779         add_visitor_type(&visitors[i]);
780         i++;
781     }
782
783     g_test_run();
784
785     return 0;
786 }