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