]> git.proxmox.com Git - qemu.git/blob - tests/test-visitor-serialization.c
qapi: fix visitor serialization tests for numbers/doubles
[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 "qapi/qmp/types.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 static void test_primitives(gconstpointer opaque)
233 {
234 TestArgs *args = (TestArgs *) opaque;
235 const SerializeOps *ops = args->ops;
236 PrimitiveType *pt = args->test_data;
237 PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy));
238 Error *err = NULL;
239 void *serialize_data;
240
241 pt_copy->type = pt->type;
242 ops->serialize(pt, &serialize_data, visit_primitive_type, &err);
243 ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, &err);
244
245 g_assert(err == NULL);
246 g_assert(pt_copy != NULL);
247 if (pt->type == PTYPE_STRING) {
248 g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string);
249 g_free((char *)pt_copy->value.string);
250 } else if (pt->type == PTYPE_NUMBER) {
251 GString *double_expected = g_string_new("");
252 GString *double_actual = g_string_new("");
253 /* we serialize with %f for our reference visitors, so rather than fuzzy
254 * floating math to test "equality", just compare the formatted values
255 */
256 g_string_printf(double_expected, "%.6f", pt->value.number);
257 g_string_printf(double_actual, "%.6f", pt_copy->value.number);
258 g_assert_cmpstr(double_actual->str, ==, double_expected->str);
259
260 g_string_free(double_expected, true);
261 g_string_free(double_actual, true);
262 } else if (pt->type == PTYPE_BOOLEAN) {
263 g_assert_cmpint(!!pt->value.max, ==, !!pt->value.max);
264 } else {
265 g_assert_cmpint(pt->value.max, ==, pt_copy->value.max);
266 }
267
268 ops->cleanup(serialize_data);
269 g_free(args);
270 g_free(pt_copy);
271 }
272
273 static void test_struct(gconstpointer opaque)
274 {
275 TestArgs *args = (TestArgs *) opaque;
276 const SerializeOps *ops = args->ops;
277 TestStruct *ts = struct_create();
278 TestStruct *ts_copy = NULL;
279 Error *err = NULL;
280 void *serialize_data;
281
282 ops->serialize(ts, &serialize_data, visit_struct, &err);
283 ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, &err);
284
285 g_assert(err == NULL);
286 struct_compare(ts, ts_copy);
287
288 struct_cleanup(ts);
289 struct_cleanup(ts_copy);
290
291 ops->cleanup(serialize_data);
292 g_free(args);
293 }
294
295 static void test_nested_struct(gconstpointer opaque)
296 {
297 TestArgs *args = (TestArgs *) opaque;
298 const SerializeOps *ops = args->ops;
299 UserDefNested *udnp = nested_struct_create();
300 UserDefNested *udnp_copy = NULL;
301 Error *err = NULL;
302 void *serialize_data;
303
304 ops->serialize(udnp, &serialize_data, visit_nested_struct, &err);
305 ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, &err);
306
307 g_assert(err == NULL);
308 nested_struct_compare(udnp, udnp_copy);
309
310 nested_struct_cleanup(udnp);
311 nested_struct_cleanup(udnp_copy);
312
313 ops->cleanup(serialize_data);
314 g_free(args);
315 }
316
317 static void test_nested_struct_list(gconstpointer opaque)
318 {
319 TestArgs *args = (TestArgs *) opaque;
320 const SerializeOps *ops = args->ops;
321 UserDefNestedList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
322 Error *err = NULL;
323 void *serialize_data;
324 int i = 0;
325
326 for (i = 0; i < 8; i++) {
327 tmp = g_malloc0(sizeof(UserDefNestedList));
328 tmp->value = nested_struct_create();
329 tmp->next = listp;
330 listp = tmp;
331 }
332
333 ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
334 ops->deserialize((void **)&listp_copy, serialize_data,
335 visit_nested_struct_list, &err);
336
337 g_assert(err == NULL);
338
339 tmp = listp;
340 tmp_copy = listp_copy;
341 while (listp_copy) {
342 g_assert(listp);
343 nested_struct_compare(listp->value, listp_copy->value);
344 listp = listp->next;
345 listp_copy = listp_copy->next;
346 }
347
348 qapi_free_UserDefNestedList(tmp);
349 qapi_free_UserDefNestedList(tmp_copy);
350
351 ops->cleanup(serialize_data);
352 g_free(args);
353 }
354
355 PrimitiveType pt_values[] = {
356 /* string tests */
357 {
358 .description = "string_empty",
359 .type = PTYPE_STRING,
360 .value.string = "",
361 },
362 {
363 .description = "string_whitespace",
364 .type = PTYPE_STRING,
365 .value.string = "a b c\td",
366 },
367 {
368 .description = "string_newlines",
369 .type = PTYPE_STRING,
370 .value.string = "a\nb\n",
371 },
372 {
373 .description = "string_commas",
374 .type = PTYPE_STRING,
375 .value.string = "a,b, c,d",
376 },
377 {
378 .description = "string_single_quoted",
379 .type = PTYPE_STRING,
380 .value.string = "'a b',cd",
381 },
382 {
383 .description = "string_double_quoted",
384 .type = PTYPE_STRING,
385 .value.string = "\"a b\",cd",
386 },
387 /* boolean tests */
388 {
389 .description = "boolean_true1",
390 .type = PTYPE_BOOLEAN,
391 .value.boolean = true,
392 },
393 {
394 .description = "boolean_true2",
395 .type = PTYPE_BOOLEAN,
396 .value.boolean = 8,
397 },
398 {
399 .description = "boolean_true3",
400 .type = PTYPE_BOOLEAN,
401 .value.boolean = -1,
402 },
403 {
404 .description = "boolean_false1",
405 .type = PTYPE_BOOLEAN,
406 .value.boolean = false,
407 },
408 {
409 .description = "boolean_false2",
410 .type = PTYPE_BOOLEAN,
411 .value.boolean = 0,
412 },
413 /* number tests (double) */
414 /* note: we format these to %.6f before comparing, since that's how
415 * we serialize them and it doesn't make sense to check precision
416 * beyond that.
417 */
418 {
419 .description = "number_sanity1",
420 .type = PTYPE_NUMBER,
421 .value.number = -1,
422 },
423 {
424 .description = "number_sanity2",
425 .type = PTYPE_NUMBER,
426 .value.number = 3.14159265,
427 },
428 {
429 .description = "number_min",
430 .type = PTYPE_NUMBER,
431 .value.number = DBL_MIN,
432 },
433 {
434 .description = "number_max",
435 .type = PTYPE_NUMBER,
436 .value.number = DBL_MAX,
437 },
438 /* integer tests (int64) */
439 {
440 .description = "integer_sanity1",
441 .type = PTYPE_INTEGER,
442 .value.integer = -1,
443 },
444 {
445 .description = "integer_sanity2",
446 .type = PTYPE_INTEGER,
447 .value.integer = INT64_MAX / 2 + 1,
448 },
449 {
450 .description = "integer_min",
451 .type = PTYPE_INTEGER,
452 .value.integer = INT64_MIN,
453 },
454 {
455 .description = "integer_max",
456 .type = PTYPE_INTEGER,
457 .value.integer = INT64_MAX,
458 },
459 /* uint8 tests */
460 {
461 .description = "uint8_sanity1",
462 .type = PTYPE_U8,
463 .value.u8 = 1,
464 },
465 {
466 .description = "uint8_sanity2",
467 .type = PTYPE_U8,
468 .value.u8 = UINT8_MAX / 2 + 1,
469 },
470 {
471 .description = "uint8_min",
472 .type = PTYPE_U8,
473 .value.u8 = 0,
474 },
475 {
476 .description = "uint8_max",
477 .type = PTYPE_U8,
478 .value.u8 = UINT8_MAX,
479 },
480 /* uint16 tests */
481 {
482 .description = "uint16_sanity1",
483 .type = PTYPE_U16,
484 .value.u16 = 1,
485 },
486 {
487 .description = "uint16_sanity2",
488 .type = PTYPE_U16,
489 .value.u16 = UINT16_MAX / 2 + 1,
490 },
491 {
492 .description = "uint16_min",
493 .type = PTYPE_U16,
494 .value.u16 = 0,
495 },
496 {
497 .description = "uint16_max",
498 .type = PTYPE_U16,
499 .value.u16 = UINT16_MAX,
500 },
501 /* uint32 tests */
502 {
503 .description = "uint32_sanity1",
504 .type = PTYPE_U32,
505 .value.u32 = 1,
506 },
507 {
508 .description = "uint32_sanity2",
509 .type = PTYPE_U32,
510 .value.u32 = UINT32_MAX / 2 + 1,
511 },
512 {
513 .description = "uint32_min",
514 .type = PTYPE_U32,
515 .value.u32 = 0,
516 },
517 {
518 .description = "uint32_max",
519 .type = PTYPE_U32,
520 .value.u32 = UINT32_MAX,
521 },
522 /* uint64 tests */
523 {
524 .description = "uint64_sanity1",
525 .type = PTYPE_U64,
526 .value.u64 = 1,
527 },
528 {
529 .description = "uint64_sanity2",
530 .type = PTYPE_U64,
531 .value.u64 = UINT64_MAX / 2 + 1,
532 },
533 {
534 .description = "uint64_min",
535 .type = PTYPE_U64,
536 .value.u64 = 0,
537 },
538 {
539 .description = "uint64_max",
540 .type = PTYPE_U64,
541 .value.u64 = UINT64_MAX,
542 },
543 /* int8 tests */
544 {
545 .description = "int8_sanity1",
546 .type = PTYPE_S8,
547 .value.s8 = -1,
548 },
549 {
550 .description = "int8_sanity2",
551 .type = PTYPE_S8,
552 .value.s8 = INT8_MAX / 2 + 1,
553 },
554 {
555 .description = "int8_min",
556 .type = PTYPE_S8,
557 .value.s8 = INT8_MIN,
558 },
559 {
560 .description = "int8_max",
561 .type = PTYPE_S8,
562 .value.s8 = INT8_MAX,
563 },
564 /* int16 tests */
565 {
566 .description = "int16_sanity1",
567 .type = PTYPE_S16,
568 .value.s16 = -1,
569 },
570 {
571 .description = "int16_sanity2",
572 .type = PTYPE_S16,
573 .value.s16 = INT16_MAX / 2 + 1,
574 },
575 {
576 .description = "int16_min",
577 .type = PTYPE_S16,
578 .value.s16 = INT16_MIN,
579 },
580 {
581 .description = "int16_max",
582 .type = PTYPE_S16,
583 .value.s16 = INT16_MAX,
584 },
585 /* int32 tests */
586 {
587 .description = "int32_sanity1",
588 .type = PTYPE_S32,
589 .value.s32 = -1,
590 },
591 {
592 .description = "int32_sanity2",
593 .type = PTYPE_S32,
594 .value.s32 = INT32_MAX / 2 + 1,
595 },
596 {
597 .description = "int32_min",
598 .type = PTYPE_S32,
599 .value.s32 = INT32_MIN,
600 },
601 {
602 .description = "int32_max",
603 .type = PTYPE_S32,
604 .value.s32 = INT32_MAX,
605 },
606 /* int64 tests */
607 {
608 .description = "int64_sanity1",
609 .type = PTYPE_S64,
610 .value.s64 = -1,
611 },
612 {
613 .description = "int64_sanity2",
614 .type = PTYPE_S64,
615 .value.s64 = INT64_MAX / 2 + 1,
616 },
617 {
618 .description = "int64_min",
619 .type = PTYPE_S64,
620 .value.s64 = INT64_MIN,
621 },
622 {
623 .description = "int64_max",
624 .type = PTYPE_S64,
625 .value.s64 = INT64_MAX,
626 },
627 { .type = PTYPE_EOL }
628 };
629
630 /* visitor-specific op implementations */
631
632 typedef struct QmpSerializeData {
633 QmpOutputVisitor *qov;
634 QmpInputVisitor *qiv;
635 } QmpSerializeData;
636
637 static void qmp_serialize(void *native_in, void **datap,
638 VisitorFunc visit, Error **errp)
639 {
640 QmpSerializeData *d = g_malloc0(sizeof(*d));
641
642 d->qov = qmp_output_visitor_new();
643 visit(qmp_output_get_visitor(d->qov), &native_in, errp);
644 *datap = d;
645 }
646
647 static void qmp_deserialize(void **native_out, void *datap,
648 VisitorFunc visit, Error **errp)
649 {
650 QmpSerializeData *d = datap;
651 QString *output_json;
652 QObject *obj_orig, *obj;
653
654 obj_orig = qmp_output_get_qobject(d->qov);
655 output_json = qobject_to_json(obj_orig);
656 obj = qobject_from_json(qstring_get_str(output_json));
657
658 QDECREF(output_json);
659 d->qiv = qmp_input_visitor_new(obj);
660 qobject_decref(obj_orig);
661 qobject_decref(obj);
662 visit(qmp_input_get_visitor(d->qiv), native_out, errp);
663 }
664
665 static void qmp_cleanup(void *datap)
666 {
667 QmpSerializeData *d = datap;
668 qmp_output_visitor_cleanup(d->qov);
669 qmp_input_visitor_cleanup(d->qiv);
670
671 g_free(d);
672 }
673
674 typedef struct StringSerializeData {
675 char *string;
676 StringOutputVisitor *sov;
677 StringInputVisitor *siv;
678 } StringSerializeData;
679
680 static void string_serialize(void *native_in, void **datap,
681 VisitorFunc visit, Error **errp)
682 {
683 StringSerializeData *d = g_malloc0(sizeof(*d));
684
685 d->sov = string_output_visitor_new();
686 visit(string_output_get_visitor(d->sov), &native_in, errp);
687 *datap = d;
688 }
689
690 static void string_deserialize(void **native_out, void *datap,
691 VisitorFunc visit, Error **errp)
692 {
693 StringSerializeData *d = datap;
694
695 d->string = string_output_get_string(d->sov);
696 d->siv = string_input_visitor_new(d->string);
697 visit(string_input_get_visitor(d->siv), native_out, errp);
698 }
699
700 static void string_cleanup(void *datap)
701 {
702 StringSerializeData *d = datap;
703
704 string_output_visitor_cleanup(d->sov);
705 string_input_visitor_cleanup(d->siv);
706 g_free(d->string);
707 g_free(d);
708 }
709
710 /* visitor registration, test harness */
711
712 /* note: to function interchangeably as a serialization mechanism your
713 * visitor test implementation should pass the test cases for all visitor
714 * capabilities: primitives, structures, and lists
715 */
716 static const SerializeOps visitors[] = {
717 {
718 .type = "QMP",
719 .serialize = qmp_serialize,
720 .deserialize = qmp_deserialize,
721 .cleanup = qmp_cleanup,
722 .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS
723 },
724 {
725 .type = "String",
726 .serialize = string_serialize,
727 .deserialize = string_deserialize,
728 .cleanup = string_cleanup,
729 .caps = VCAP_PRIMITIVES
730 },
731 { NULL }
732 };
733
734 static void add_visitor_type(const SerializeOps *ops)
735 {
736 char testname_prefix[128];
737 char testname[128];
738 TestArgs *args;
739 int i = 0;
740
741 sprintf(testname_prefix, "/visitor/serialization/%s", ops->type);
742
743 if (ops->caps & VCAP_PRIMITIVES) {
744 while (pt_values[i].type != PTYPE_EOL) {
745 sprintf(testname, "%s/primitives/%s", testname_prefix,
746 pt_values[i].description);
747 args = g_malloc0(sizeof(*args));
748 args->ops = ops;
749 args->test_data = &pt_values[i];
750 g_test_add_data_func(testname, args, test_primitives);
751 i++;
752 }
753 }
754
755 if (ops->caps & VCAP_STRUCTURES) {
756 sprintf(testname, "%s/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_struct);
761
762 sprintf(testname, "%s/nested_struct", testname_prefix);
763 args = g_malloc0(sizeof(*args));
764 args->ops = ops;
765 args->test_data = NULL;
766 g_test_add_data_func(testname, args, test_nested_struct);
767 }
768
769 if (ops->caps & VCAP_LISTS) {
770 sprintf(testname, "%s/nested_struct_list", testname_prefix);
771 args = g_malloc0(sizeof(*args));
772 args->ops = ops;
773 args->test_data = NULL;
774 g_test_add_data_func(testname, args, test_nested_struct_list);
775 }
776 }
777
778 int main(int argc, char **argv)
779 {
780 int i = 0;
781
782 g_test_init(&argc, &argv, NULL);
783
784 while (visitors[i].type != NULL) {
785 add_visitor_type(&visitors[i]);
786 i++;
787 }
788
789 g_test_run();
790
791 return 0;
792 }