]>
git.proxmox.com Git - qemu.git/blob - tests/test-visitor-serialization.c
2 * Unit-tests for visitor-based serialization
4 * Copyright IBM, Corp. 2012
7 * Michael Roth <mdroth@linux.vnet.ibm.com>
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.
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"
27 typedef struct PrimitiveType
{
58 const char *description
;
63 static void visit_primitive_type(Visitor
*v
, void **native
, Error
**errp
)
65 PrimitiveType
*pt
= *native
;
68 visit_type_str(v
, (char **)&pt
->value
.string
, NULL
, errp
);
71 visit_type_bool(v
, &pt
->value
.boolean
, NULL
, errp
);
74 visit_type_number(v
, &pt
->value
.number
, NULL
, errp
);
77 visit_type_int(v
, &pt
->value
.integer
, NULL
, errp
);
80 visit_type_uint8(v
, &pt
->value
.u8
, NULL
, errp
);
83 visit_type_uint16(v
, &pt
->value
.u16
, NULL
, errp
);
86 visit_type_uint32(v
, &pt
->value
.u32
, NULL
, errp
);
89 visit_type_uint64(v
, &pt
->value
.u64
, NULL
, errp
);
92 visit_type_int8(v
, &pt
->value
.s8
, NULL
, errp
);
95 visit_type_int16(v
, &pt
->value
.s16
, NULL
, errp
);
98 visit_type_int32(v
, &pt
->value
.s32
, NULL
, errp
);
101 visit_type_int64(v
, &pt
->value
.s64
, NULL
, errp
);
108 typedef struct TestStruct
115 static void visit_type_TestStruct(Visitor
*v
, TestStruct
**obj
,
116 const char *name
, Error
**errp
)
118 visit_start_struct(v
, (void **)obj
, NULL
, name
, sizeof(TestStruct
), errp
);
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
);
124 visit_end_struct(v
, errp
);
127 static TestStruct
*struct_create(void)
129 TestStruct
*ts
= g_malloc0(sizeof(*ts
));
132 ts
->string
= strdup("test string");
136 static void struct_compare(TestStruct
*ts1
, TestStruct
*ts2
)
140 g_assert_cmpint(ts1
->integer
, ==, ts2
->integer
);
141 g_assert(ts1
->boolean
== ts2
->boolean
);
142 g_assert_cmpstr(ts1
->string
, ==, ts2
->string
);
145 static void struct_cleanup(TestStruct
*ts
)
151 static void visit_struct(Visitor
*v
, void **native
, Error
**errp
)
153 visit_type_TestStruct(v
, (TestStruct
**)native
, NULL
, errp
);
156 static UserDefNested
*nested_struct_create(void)
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");
173 static void nested_struct_compare(UserDefNested
*udnp1
, UserDefNested
*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
);
192 static void nested_struct_cleanup(UserDefNested
*udnp
)
194 qapi_free_UserDefNested(udnp
);
197 static void visit_nested_struct(Visitor
*v
, void **native
, Error
**errp
)
199 visit_type_UserDefNested(v
, (UserDefNested
**)native
, NULL
, errp
);
202 static void visit_nested_struct_list(Visitor
*v
, void **native
, Error
**errp
)
204 visit_type_UserDefNestedList(v
, (UserDefNestedList
**)native
, NULL
, errp
);
209 typedef void (*VisitorFunc
)(Visitor
*v
, void **native
, Error
**errp
);
211 typedef enum VisitorCapabilities
{
215 } VisitorCapabilities
;
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
);
224 VisitorCapabilities caps
;
227 typedef struct TestArgs
{
228 const SerializeOps
*ops
;
232 static void test_primitives(gconstpointer opaque
)
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
));
239 void *serialize_data
;
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
);
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
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
);
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
);
265 g_assert_cmpint(pt
->value
.max
, ==, pt_copy
->value
.max
);
268 ops
->cleanup(serialize_data
);
273 static void test_struct(gconstpointer opaque
)
275 TestArgs
*args
= (TestArgs
*) opaque
;
276 const SerializeOps
*ops
= args
->ops
;
277 TestStruct
*ts
= struct_create();
278 TestStruct
*ts_copy
= NULL
;
280 void *serialize_data
;
282 ops
->serialize(ts
, &serialize_data
, visit_struct
, &err
);
283 ops
->deserialize((void **)&ts_copy
, serialize_data
, visit_struct
, &err
);
285 g_assert(err
== NULL
);
286 struct_compare(ts
, ts_copy
);
289 struct_cleanup(ts_copy
);
291 ops
->cleanup(serialize_data
);
295 static void test_nested_struct(gconstpointer opaque
)
297 TestArgs
*args
= (TestArgs
*) opaque
;
298 const SerializeOps
*ops
= args
->ops
;
299 UserDefNested
*udnp
= nested_struct_create();
300 UserDefNested
*udnp_copy
= NULL
;
302 void *serialize_data
;
304 ops
->serialize(udnp
, &serialize_data
, visit_nested_struct
, &err
);
305 ops
->deserialize((void **)&udnp_copy
, serialize_data
, visit_nested_struct
, &err
);
307 g_assert(err
== NULL
);
308 nested_struct_compare(udnp
, udnp_copy
);
310 nested_struct_cleanup(udnp
);
311 nested_struct_cleanup(udnp_copy
);
313 ops
->cleanup(serialize_data
);
317 static void test_nested_struct_list(gconstpointer opaque
)
319 TestArgs
*args
= (TestArgs
*) opaque
;
320 const SerializeOps
*ops
= args
->ops
;
321 UserDefNestedList
*listp
= NULL
, *tmp
, *tmp_copy
, *listp_copy
= NULL
;
323 void *serialize_data
;
326 for (i
= 0; i
< 8; i
++) {
327 tmp
= g_malloc0(sizeof(UserDefNestedList
));
328 tmp
->value
= nested_struct_create();
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
);
337 g_assert(err
== NULL
);
340 tmp_copy
= listp_copy
;
343 nested_struct_compare(listp
->value
, listp_copy
->value
);
345 listp_copy
= listp_copy
->next
;
348 qapi_free_UserDefNestedList(tmp
);
349 qapi_free_UserDefNestedList(tmp_copy
);
351 ops
->cleanup(serialize_data
);
355 PrimitiveType pt_values
[] = {
358 .description
= "string_empty",
359 .type
= PTYPE_STRING
,
363 .description
= "string_whitespace",
364 .type
= PTYPE_STRING
,
365 .value
.string
= "a b c\td",
368 .description
= "string_newlines",
369 .type
= PTYPE_STRING
,
370 .value
.string
= "a\nb\n",
373 .description
= "string_commas",
374 .type
= PTYPE_STRING
,
375 .value
.string
= "a,b, c,d",
378 .description
= "string_single_quoted",
379 .type
= PTYPE_STRING
,
380 .value
.string
= "'a b',cd",
383 .description
= "string_double_quoted",
384 .type
= PTYPE_STRING
,
385 .value
.string
= "\"a b\",cd",
389 .description
= "boolean_true1",
390 .type
= PTYPE_BOOLEAN
,
391 .value
.boolean
= true,
394 .description
= "boolean_true2",
395 .type
= PTYPE_BOOLEAN
,
399 .description
= "boolean_true3",
400 .type
= PTYPE_BOOLEAN
,
404 .description
= "boolean_false1",
405 .type
= PTYPE_BOOLEAN
,
406 .value
.boolean
= false,
409 .description
= "boolean_false2",
410 .type
= PTYPE_BOOLEAN
,
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
419 .description
= "number_sanity1",
420 .type
= PTYPE_NUMBER
,
424 .description
= "number_sanity2",
425 .type
= PTYPE_NUMBER
,
426 .value
.number
= 3.14159265,
429 .description
= "number_min",
430 .type
= PTYPE_NUMBER
,
431 .value
.number
= DBL_MIN
,
434 .description
= "number_max",
435 .type
= PTYPE_NUMBER
,
436 .value
.number
= DBL_MAX
,
438 /* integer tests (int64) */
440 .description
= "integer_sanity1",
441 .type
= PTYPE_INTEGER
,
445 .description
= "integer_sanity2",
446 .type
= PTYPE_INTEGER
,
447 .value
.integer
= INT64_MAX
/ 2 + 1,
450 .description
= "integer_min",
451 .type
= PTYPE_INTEGER
,
452 .value
.integer
= INT64_MIN
,
455 .description
= "integer_max",
456 .type
= PTYPE_INTEGER
,
457 .value
.integer
= INT64_MAX
,
461 .description
= "uint8_sanity1",
466 .description
= "uint8_sanity2",
468 .value
.u8
= UINT8_MAX
/ 2 + 1,
471 .description
= "uint8_min",
476 .description
= "uint8_max",
478 .value
.u8
= UINT8_MAX
,
482 .description
= "uint16_sanity1",
487 .description
= "uint16_sanity2",
489 .value
.u16
= UINT16_MAX
/ 2 + 1,
492 .description
= "uint16_min",
497 .description
= "uint16_max",
499 .value
.u16
= UINT16_MAX
,
503 .description
= "uint32_sanity1",
508 .description
= "uint32_sanity2",
510 .value
.u32
= UINT32_MAX
/ 2 + 1,
513 .description
= "uint32_min",
518 .description
= "uint32_max",
520 .value
.u32
= UINT32_MAX
,
524 .description
= "uint64_sanity1",
529 .description
= "uint64_sanity2",
531 .value
.u64
= UINT64_MAX
/ 2 + 1,
534 .description
= "uint64_min",
539 .description
= "uint64_max",
541 .value
.u64
= UINT64_MAX
,
545 .description
= "int8_sanity1",
550 .description
= "int8_sanity2",
552 .value
.s8
= INT8_MAX
/ 2 + 1,
555 .description
= "int8_min",
557 .value
.s8
= INT8_MIN
,
560 .description
= "int8_max",
562 .value
.s8
= INT8_MAX
,
566 .description
= "int16_sanity1",
571 .description
= "int16_sanity2",
573 .value
.s16
= INT16_MAX
/ 2 + 1,
576 .description
= "int16_min",
578 .value
.s16
= INT16_MIN
,
581 .description
= "int16_max",
583 .value
.s16
= INT16_MAX
,
587 .description
= "int32_sanity1",
592 .description
= "int32_sanity2",
594 .value
.s32
= INT32_MAX
/ 2 + 1,
597 .description
= "int32_min",
599 .value
.s32
= INT32_MIN
,
602 .description
= "int32_max",
604 .value
.s32
= INT32_MAX
,
608 .description
= "int64_sanity1",
613 .description
= "int64_sanity2",
615 .value
.s64
= INT64_MAX
/ 2 + 1,
618 .description
= "int64_min",
620 .value
.s64
= INT64_MIN
,
623 .description
= "int64_max",
625 .value
.s64
= INT64_MAX
,
627 { .type
= PTYPE_EOL
}
630 /* visitor-specific op implementations */
632 typedef struct QmpSerializeData
{
633 QmpOutputVisitor
*qov
;
634 QmpInputVisitor
*qiv
;
637 static void qmp_serialize(void *native_in
, void **datap
,
638 VisitorFunc visit
, Error
**errp
)
640 QmpSerializeData
*d
= g_malloc0(sizeof(*d
));
642 d
->qov
= qmp_output_visitor_new();
643 visit(qmp_output_get_visitor(d
->qov
), &native_in
, errp
);
647 static void qmp_deserialize(void **native_out
, void *datap
,
648 VisitorFunc visit
, Error
**errp
)
650 QmpSerializeData
*d
= datap
;
651 QString
*output_json
;
652 QObject
*obj_orig
, *obj
;
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
));
658 QDECREF(output_json
);
659 d
->qiv
= qmp_input_visitor_new(obj
);
660 qobject_decref(obj_orig
);
662 visit(qmp_input_get_visitor(d
->qiv
), native_out
, errp
);
665 static void qmp_cleanup(void *datap
)
667 QmpSerializeData
*d
= datap
;
668 qmp_output_visitor_cleanup(d
->qov
);
669 qmp_input_visitor_cleanup(d
->qiv
);
674 typedef struct StringSerializeData
{
676 StringOutputVisitor
*sov
;
677 StringInputVisitor
*siv
;
678 } StringSerializeData
;
680 static void string_serialize(void *native_in
, void **datap
,
681 VisitorFunc visit
, Error
**errp
)
683 StringSerializeData
*d
= g_malloc0(sizeof(*d
));
685 d
->sov
= string_output_visitor_new();
686 visit(string_output_get_visitor(d
->sov
), &native_in
, errp
);
690 static void string_deserialize(void **native_out
, void *datap
,
691 VisitorFunc visit
, Error
**errp
)
693 StringSerializeData
*d
= datap
;
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
);
700 static void string_cleanup(void *datap
)
702 StringSerializeData
*d
= datap
;
704 string_output_visitor_cleanup(d
->sov
);
705 string_input_visitor_cleanup(d
->siv
);
710 /* visitor registration, test harness */
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
716 static const SerializeOps visitors
[] = {
719 .serialize
= qmp_serialize
,
720 .deserialize
= qmp_deserialize
,
721 .cleanup
= qmp_cleanup
,
722 .caps
= VCAP_PRIMITIVES
| VCAP_STRUCTURES
| VCAP_LISTS
726 .serialize
= string_serialize
,
727 .deserialize
= string_deserialize
,
728 .cleanup
= string_cleanup
,
729 .caps
= VCAP_PRIMITIVES
734 static void add_visitor_type(const SerializeOps
*ops
)
736 char testname_prefix
[128];
741 sprintf(testname_prefix
, "/visitor/serialization/%s", ops
->type
);
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
));
749 args
->test_data
= &pt_values
[i
];
750 g_test_add_data_func(testname
, args
, test_primitives
);
755 if (ops
->caps
& VCAP_STRUCTURES
) {
756 sprintf(testname
, "%s/struct", testname_prefix
);
757 args
= g_malloc0(sizeof(*args
));
759 args
->test_data
= NULL
;
760 g_test_add_data_func(testname
, args
, test_struct
);
762 sprintf(testname
, "%s/nested_struct", testname_prefix
);
763 args
= g_malloc0(sizeof(*args
));
765 args
->test_data
= NULL
;
766 g_test_add_data_func(testname
, args
, test_nested_struct
);
769 if (ops
->caps
& VCAP_LISTS
) {
770 sprintf(testname
, "%s/nested_struct_list", testname_prefix
);
771 args
= g_malloc0(sizeof(*args
));
773 args
->test_data
= NULL
;
774 g_test_add_data_func(testname
, args
, test_nested_struct_list
);
778 int main(int argc
, char **argv
)
782 g_test_init(&argc
, &argv
, NULL
);
784 while (visitors
[i
].type
!= NULL
) {
785 add_visitor_type(&visitors
[i
]);