]> git.proxmox.com Git - mirror_qemu.git/blob - tests/test-visitor-serialization.c
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
[mirror_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 #include "qapi-types.h"
27 #include "qapi-visit.h"
28 #include "qapi/dealloc-visitor.h"
29
30 enum PrimitiveTypeKind {
31 PTYPE_STRING = 0,
32 PTYPE_BOOLEAN,
33 PTYPE_NUMBER,
34 PTYPE_INTEGER,
35 PTYPE_U8,
36 PTYPE_U16,
37 PTYPE_U32,
38 PTYPE_U64,
39 PTYPE_S8,
40 PTYPE_S16,
41 PTYPE_S32,
42 PTYPE_S64,
43 PTYPE_EOL,
44 };
45
46 typedef struct PrimitiveType {
47 union {
48 const char *string;
49 bool boolean;
50 double number;
51 int64_t integer;
52 uint8_t u8;
53 uint16_t u16;
54 uint32_t u32;
55 uint64_t u64;
56 int8_t s8;
57 int16_t s16;
58 int32_t s32;
59 int64_t s64;
60 intmax_t max;
61 } value;
62 enum PrimitiveTypeKind type;
63 const char *description;
64 } PrimitiveType;
65
66 typedef struct PrimitiveList {
67 union {
68 strList *strings;
69 boolList *booleans;
70 numberList *numbers;
71 intList *integers;
72 int8List *s8_integers;
73 int16List *s16_integers;
74 int32List *s32_integers;
75 int64List *s64_integers;
76 uint8List *u8_integers;
77 uint16List *u16_integers;
78 uint32List *u32_integers;
79 uint64List *u64_integers;
80 } value;
81 enum PrimitiveTypeKind type;
82 const char *description;
83 } PrimitiveList;
84
85 /* test helpers */
86
87 typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp);
88
89 static void dealloc_helper(void *native_in, VisitorFunc visit, Error **errp)
90 {
91 QapiDeallocVisitor *qdv = qapi_dealloc_visitor_new();
92
93 visit(qapi_dealloc_get_visitor(qdv), &native_in, errp);
94
95 qapi_dealloc_visitor_cleanup(qdv);
96 }
97
98 static void visit_primitive_type(Visitor *v, void **native, Error **errp)
99 {
100 PrimitiveType *pt = *native;
101 switch(pt->type) {
102 case PTYPE_STRING:
103 visit_type_str(v, (char **)&pt->value.string, NULL, errp);
104 break;
105 case PTYPE_BOOLEAN:
106 visit_type_bool(v, &pt->value.boolean, NULL, errp);
107 break;
108 case PTYPE_NUMBER:
109 visit_type_number(v, &pt->value.number, NULL, errp);
110 break;
111 case PTYPE_INTEGER:
112 visit_type_int(v, &pt->value.integer, NULL, errp);
113 break;
114 case PTYPE_U8:
115 visit_type_uint8(v, &pt->value.u8, NULL, errp);
116 break;
117 case PTYPE_U16:
118 visit_type_uint16(v, &pt->value.u16, NULL, errp);
119 break;
120 case PTYPE_U32:
121 visit_type_uint32(v, &pt->value.u32, NULL, errp);
122 break;
123 case PTYPE_U64:
124 visit_type_uint64(v, &pt->value.u64, NULL, errp);
125 break;
126 case PTYPE_S8:
127 visit_type_int8(v, &pt->value.s8, NULL, errp);
128 break;
129 case PTYPE_S16:
130 visit_type_int16(v, &pt->value.s16, NULL, errp);
131 break;
132 case PTYPE_S32:
133 visit_type_int32(v, &pt->value.s32, NULL, errp);
134 break;
135 case PTYPE_S64:
136 visit_type_int64(v, &pt->value.s64, NULL, errp);
137 break;
138 case PTYPE_EOL:
139 g_assert_not_reached();
140 }
141 }
142
143 static void visit_primitive_list(Visitor *v, void **native, Error **errp)
144 {
145 PrimitiveList *pl = *native;
146 switch (pl->type) {
147 case PTYPE_STRING:
148 visit_type_strList(v, &pl->value.strings, NULL, errp);
149 break;
150 case PTYPE_BOOLEAN:
151 visit_type_boolList(v, &pl->value.booleans, NULL, errp);
152 break;
153 case PTYPE_NUMBER:
154 visit_type_numberList(v, &pl->value.numbers, NULL, errp);
155 break;
156 case PTYPE_INTEGER:
157 visit_type_intList(v, &pl->value.integers, NULL, errp);
158 break;
159 case PTYPE_S8:
160 visit_type_int8List(v, &pl->value.s8_integers, NULL, errp);
161 break;
162 case PTYPE_S16:
163 visit_type_int16List(v, &pl->value.s16_integers, NULL, errp);
164 break;
165 case PTYPE_S32:
166 visit_type_int32List(v, &pl->value.s32_integers, NULL, errp);
167 break;
168 case PTYPE_S64:
169 visit_type_int64List(v, &pl->value.s64_integers, NULL, errp);
170 break;
171 case PTYPE_U8:
172 visit_type_uint8List(v, &pl->value.u8_integers, NULL, errp);
173 break;
174 case PTYPE_U16:
175 visit_type_uint16List(v, &pl->value.u16_integers, NULL, errp);
176 break;
177 case PTYPE_U32:
178 visit_type_uint32List(v, &pl->value.u32_integers, NULL, errp);
179 break;
180 case PTYPE_U64:
181 visit_type_uint64List(v, &pl->value.u64_integers, NULL, errp);
182 break;
183 default:
184 g_assert_not_reached();
185 }
186 }
187
188 typedef struct TestStruct
189 {
190 int64_t integer;
191 bool boolean;
192 char *string;
193 } TestStruct;
194
195 static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
196 const char *name, Error **errp)
197 {
198 Error *err = NULL;
199
200 visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err);
201 if (err) {
202 goto out;
203 }
204
205 visit_type_int(v, &(*obj)->integer, "integer", &err);
206 if (err) {
207 goto out_end;
208 }
209 visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
210 if (err) {
211 goto out_end;
212 }
213 visit_type_str(v, &(*obj)->string, "string", &err);
214
215 out_end:
216 error_propagate(errp, err);
217 err = NULL;
218 visit_end_struct(v, &err);
219 out:
220 error_propagate(errp, err);
221 }
222
223 static TestStruct *struct_create(void)
224 {
225 TestStruct *ts = g_malloc0(sizeof(*ts));
226 ts->integer = -42;
227 ts->boolean = true;
228 ts->string = strdup("test string");
229 return ts;
230 }
231
232 static void struct_compare(TestStruct *ts1, TestStruct *ts2)
233 {
234 g_assert(ts1);
235 g_assert(ts2);
236 g_assert_cmpint(ts1->integer, ==, ts2->integer);
237 g_assert(ts1->boolean == ts2->boolean);
238 g_assert_cmpstr(ts1->string, ==, ts2->string);
239 }
240
241 static void struct_cleanup(TestStruct *ts)
242 {
243 g_free(ts->string);
244 g_free(ts);
245 }
246
247 static void visit_struct(Visitor *v, void **native, Error **errp)
248 {
249 visit_type_TestStruct(v, (TestStruct **)native, NULL, errp);
250 }
251
252 static UserDefNested *nested_struct_create(void)
253 {
254 UserDefNested *udnp = g_malloc0(sizeof(*udnp));
255 udnp->string0 = strdup("test_string0");
256 udnp->dict1.string1 = strdup("test_string1");
257 udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
258 udnp->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1);
259 udnp->dict1.dict2.userdef1->base->integer = 42;
260 udnp->dict1.dict2.userdef1->string = strdup("test_string");
261 udnp->dict1.dict2.string2 = strdup("test_string2");
262 udnp->dict1.has_dict3 = true;
263 udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
264 udnp->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1);
265 udnp->dict1.dict3.userdef2->base->integer = 43;
266 udnp->dict1.dict3.userdef2->string = strdup("test_string");
267 udnp->dict1.dict3.string3 = strdup("test_string3");
268 return udnp;
269 }
270
271 static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2)
272 {
273 g_assert(udnp1);
274 g_assert(udnp2);
275 g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
276 g_assert_cmpstr(udnp1->dict1.string1, ==, udnp2->dict1.string1);
277 g_assert_cmpint(udnp1->dict1.dict2.userdef1->base->integer, ==,
278 udnp2->dict1.dict2.userdef1->base->integer);
279 g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==,
280 udnp2->dict1.dict2.userdef1->string);
281 g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2);
282 g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3);
283 g_assert_cmpint(udnp1->dict1.dict3.userdef2->base->integer, ==,
284 udnp2->dict1.dict3.userdef2->base->integer);
285 g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==,
286 udnp2->dict1.dict3.userdef2->string);
287 g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3);
288 }
289
290 static void nested_struct_cleanup(UserDefNested *udnp)
291 {
292 qapi_free_UserDefNested(udnp);
293 }
294
295 static void visit_nested_struct(Visitor *v, void **native, Error **errp)
296 {
297 visit_type_UserDefNested(v, (UserDefNested **)native, NULL, errp);
298 }
299
300 static void visit_nested_struct_list(Visitor *v, void **native, Error **errp)
301 {
302 visit_type_UserDefNestedList(v, (UserDefNestedList **)native, NULL, errp);
303 }
304
305 /* test cases */
306
307 typedef enum VisitorCapabilities {
308 VCAP_PRIMITIVES = 1,
309 VCAP_STRUCTURES = 2,
310 VCAP_LISTS = 4,
311 VCAP_PRIMITIVE_LISTS = 8,
312 } VisitorCapabilities;
313
314 typedef struct SerializeOps {
315 void (*serialize)(void *native_in, void **datap,
316 VisitorFunc visit, Error **errp);
317 void (*deserialize)(void **native_out, void *datap,
318 VisitorFunc visit, Error **errp);
319 void (*cleanup)(void *datap);
320 const char *type;
321 VisitorCapabilities caps;
322 } SerializeOps;
323
324 typedef struct TestArgs {
325 const SerializeOps *ops;
326 void *test_data;
327 } TestArgs;
328
329 static void test_primitives(gconstpointer opaque)
330 {
331 TestArgs *args = (TestArgs *) opaque;
332 const SerializeOps *ops = args->ops;
333 PrimitiveType *pt = args->test_data;
334 PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy));
335 Error *err = NULL;
336 void *serialize_data;
337
338 pt_copy->type = pt->type;
339 ops->serialize(pt, &serialize_data, visit_primitive_type, &err);
340 ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, &err);
341
342 g_assert(err == NULL);
343 g_assert(pt_copy != NULL);
344 if (pt->type == PTYPE_STRING) {
345 g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string);
346 g_free((char *)pt_copy->value.string);
347 } else if (pt->type == PTYPE_NUMBER) {
348 GString *double_expected = g_string_new("");
349 GString *double_actual = g_string_new("");
350 /* we serialize with %f for our reference visitors, so rather than fuzzy
351 * floating math to test "equality", just compare the formatted values
352 */
353 g_string_printf(double_expected, "%.6f", pt->value.number);
354 g_string_printf(double_actual, "%.6f", pt_copy->value.number);
355 g_assert_cmpstr(double_actual->str, ==, double_expected->str);
356
357 g_string_free(double_expected, true);
358 g_string_free(double_actual, true);
359 } else if (pt->type == PTYPE_BOOLEAN) {
360 g_assert_cmpint(!!pt->value.max, ==, !!pt->value.max);
361 } else {
362 g_assert_cmpint(pt->value.max, ==, pt_copy->value.max);
363 }
364
365 ops->cleanup(serialize_data);
366 g_free(args);
367 g_free(pt_copy);
368 }
369
370 static void test_primitive_lists(gconstpointer opaque)
371 {
372 TestArgs *args = (TestArgs *) opaque;
373 const SerializeOps *ops = args->ops;
374 PrimitiveType *pt = args->test_data;
375 PrimitiveList pl = { .value = { NULL } };
376 PrimitiveList pl_copy = { .value = { NULL } };
377 PrimitiveList *pl_copy_ptr = &pl_copy;
378 Error *err = NULL;
379 void *serialize_data;
380 void *cur_head = NULL;
381 int i;
382
383 pl.type = pl_copy.type = pt->type;
384
385 /* build up our list of primitive types */
386 for (i = 0; i < 32; i++) {
387 switch (pl.type) {
388 case PTYPE_STRING: {
389 strList *tmp = g_new0(strList, 1);
390 tmp->value = g_strdup(pt->value.string);
391 if (pl.value.strings == NULL) {
392 pl.value.strings = tmp;
393 } else {
394 tmp->next = pl.value.strings;
395 pl.value.strings = tmp;
396 }
397 break;
398 }
399 case PTYPE_INTEGER: {
400 intList *tmp = g_new0(intList, 1);
401 tmp->value = pt->value.integer;
402 if (pl.value.integers == NULL) {
403 pl.value.integers = tmp;
404 } else {
405 tmp->next = pl.value.integers;
406 pl.value.integers = tmp;
407 }
408 break;
409 }
410 case PTYPE_S8: {
411 int8List *tmp = g_new0(int8List, 1);
412 tmp->value = pt->value.s8;
413 if (pl.value.s8_integers == NULL) {
414 pl.value.s8_integers = tmp;
415 } else {
416 tmp->next = pl.value.s8_integers;
417 pl.value.s8_integers = tmp;
418 }
419 break;
420 }
421 case PTYPE_S16: {
422 int16List *tmp = g_new0(int16List, 1);
423 tmp->value = pt->value.s16;
424 if (pl.value.s16_integers == NULL) {
425 pl.value.s16_integers = tmp;
426 } else {
427 tmp->next = pl.value.s16_integers;
428 pl.value.s16_integers = tmp;
429 }
430 break;
431 }
432 case PTYPE_S32: {
433 int32List *tmp = g_new0(int32List, 1);
434 tmp->value = pt->value.s32;
435 if (pl.value.s32_integers == NULL) {
436 pl.value.s32_integers = tmp;
437 } else {
438 tmp->next = pl.value.s32_integers;
439 pl.value.s32_integers = tmp;
440 }
441 break;
442 }
443 case PTYPE_S64: {
444 int64List *tmp = g_new0(int64List, 1);
445 tmp->value = pt->value.s64;
446 if (pl.value.s64_integers == NULL) {
447 pl.value.s64_integers = tmp;
448 } else {
449 tmp->next = pl.value.s64_integers;
450 pl.value.s64_integers = tmp;
451 }
452 break;
453 }
454 case PTYPE_U8: {
455 uint8List *tmp = g_new0(uint8List, 1);
456 tmp->value = pt->value.u8;
457 if (pl.value.u8_integers == NULL) {
458 pl.value.u8_integers = tmp;
459 } else {
460 tmp->next = pl.value.u8_integers;
461 pl.value.u8_integers = tmp;
462 }
463 break;
464 }
465 case PTYPE_U16: {
466 uint16List *tmp = g_new0(uint16List, 1);
467 tmp->value = pt->value.u16;
468 if (pl.value.u16_integers == NULL) {
469 pl.value.u16_integers = tmp;
470 } else {
471 tmp->next = pl.value.u16_integers;
472 pl.value.u16_integers = tmp;
473 }
474 break;
475 }
476 case PTYPE_U32: {
477 uint32List *tmp = g_new0(uint32List, 1);
478 tmp->value = pt->value.u32;
479 if (pl.value.u32_integers == NULL) {
480 pl.value.u32_integers = tmp;
481 } else {
482 tmp->next = pl.value.u32_integers;
483 pl.value.u32_integers = tmp;
484 }
485 break;
486 }
487 case PTYPE_U64: {
488 uint64List *tmp = g_new0(uint64List, 1);
489 tmp->value = pt->value.u64;
490 if (pl.value.u64_integers == NULL) {
491 pl.value.u64_integers = tmp;
492 } else {
493 tmp->next = pl.value.u64_integers;
494 pl.value.u64_integers = tmp;
495 }
496 break;
497 }
498 case PTYPE_NUMBER: {
499 numberList *tmp = g_new0(numberList, 1);
500 tmp->value = pt->value.number;
501 if (pl.value.numbers == NULL) {
502 pl.value.numbers = tmp;
503 } else {
504 tmp->next = pl.value.numbers;
505 pl.value.numbers = tmp;
506 }
507 break;
508 }
509 case PTYPE_BOOLEAN: {
510 boolList *tmp = g_new0(boolList, 1);
511 tmp->value = pt->value.boolean;
512 if (pl.value.booleans == NULL) {
513 pl.value.booleans = tmp;
514 } else {
515 tmp->next = pl.value.booleans;
516 pl.value.booleans = tmp;
517 }
518 break;
519 }
520 default:
521 g_assert_not_reached();
522 }
523 }
524
525 ops->serialize((void **)&pl, &serialize_data, visit_primitive_list, &err);
526 ops->deserialize((void **)&pl_copy_ptr, serialize_data, visit_primitive_list, &err);
527
528 g_assert(err == NULL);
529 i = 0;
530
531 /* compare our deserialized list of primitives to the original */
532 do {
533 switch (pl_copy.type) {
534 case PTYPE_STRING: {
535 strList *ptr;
536 if (cur_head) {
537 ptr = cur_head;
538 cur_head = ptr->next;
539 } else {
540 cur_head = ptr = pl_copy.value.strings;
541 }
542 g_assert_cmpstr(pt->value.string, ==, ptr->value);
543 break;
544 }
545 case PTYPE_INTEGER: {
546 intList *ptr;
547 if (cur_head) {
548 ptr = cur_head;
549 cur_head = ptr->next;
550 } else {
551 cur_head = ptr = pl_copy.value.integers;
552 }
553 g_assert_cmpint(pt->value.integer, ==, ptr->value);
554 break;
555 }
556 case PTYPE_S8: {
557 int8List *ptr;
558 if (cur_head) {
559 ptr = cur_head;
560 cur_head = ptr->next;
561 } else {
562 cur_head = ptr = pl_copy.value.s8_integers;
563 }
564 g_assert_cmpint(pt->value.s8, ==, ptr->value);
565 break;
566 }
567 case PTYPE_S16: {
568 int16List *ptr;
569 if (cur_head) {
570 ptr = cur_head;
571 cur_head = ptr->next;
572 } else {
573 cur_head = ptr = pl_copy.value.s16_integers;
574 }
575 g_assert_cmpint(pt->value.s16, ==, ptr->value);
576 break;
577 }
578 case PTYPE_S32: {
579 int32List *ptr;
580 if (cur_head) {
581 ptr = cur_head;
582 cur_head = ptr->next;
583 } else {
584 cur_head = ptr = pl_copy.value.s32_integers;
585 }
586 g_assert_cmpint(pt->value.s32, ==, ptr->value);
587 break;
588 }
589 case PTYPE_S64: {
590 int64List *ptr;
591 if (cur_head) {
592 ptr = cur_head;
593 cur_head = ptr->next;
594 } else {
595 cur_head = ptr = pl_copy.value.s64_integers;
596 }
597 g_assert_cmpint(pt->value.s64, ==, ptr->value);
598 break;
599 }
600 case PTYPE_U8: {
601 uint8List *ptr;
602 if (cur_head) {
603 ptr = cur_head;
604 cur_head = ptr->next;
605 } else {
606 cur_head = ptr = pl_copy.value.u8_integers;
607 }
608 g_assert_cmpint(pt->value.u8, ==, ptr->value);
609 break;
610 }
611 case PTYPE_U16: {
612 uint16List *ptr;
613 if (cur_head) {
614 ptr = cur_head;
615 cur_head = ptr->next;
616 } else {
617 cur_head = ptr = pl_copy.value.u16_integers;
618 }
619 g_assert_cmpint(pt->value.u16, ==, ptr->value);
620 break;
621 }
622 case PTYPE_U32: {
623 uint32List *ptr;
624 if (cur_head) {
625 ptr = cur_head;
626 cur_head = ptr->next;
627 } else {
628 cur_head = ptr = pl_copy.value.u32_integers;
629 }
630 g_assert_cmpint(pt->value.u32, ==, ptr->value);
631 break;
632 }
633 case PTYPE_U64: {
634 uint64List *ptr;
635 if (cur_head) {
636 ptr = cur_head;
637 cur_head = ptr->next;
638 } else {
639 cur_head = ptr = pl_copy.value.u64_integers;
640 }
641 g_assert_cmpint(pt->value.u64, ==, ptr->value);
642 break;
643 }
644 case PTYPE_NUMBER: {
645 numberList *ptr;
646 GString *double_expected = g_string_new("");
647 GString *double_actual = g_string_new("");
648 if (cur_head) {
649 ptr = cur_head;
650 cur_head = ptr->next;
651 } else {
652 cur_head = ptr = pl_copy.value.numbers;
653 }
654 /* we serialize with %f for our reference visitors, so rather than
655 * fuzzy floating math to test "equality", just compare the
656 * formatted values
657 */
658 g_string_printf(double_expected, "%.6f", pt->value.number);
659 g_string_printf(double_actual, "%.6f", ptr->value);
660 g_assert_cmpstr(double_actual->str, ==, double_expected->str);
661 g_string_free(double_expected, true);
662 g_string_free(double_actual, true);
663 break;
664 }
665 case PTYPE_BOOLEAN: {
666 boolList *ptr;
667 if (cur_head) {
668 ptr = cur_head;
669 cur_head = ptr->next;
670 } else {
671 cur_head = ptr = pl_copy.value.booleans;
672 }
673 g_assert_cmpint(!!pt->value.boolean, ==, !!ptr->value);
674 break;
675 }
676 default:
677 g_assert_not_reached();
678 }
679 i++;
680 } while (cur_head);
681
682 g_assert_cmpint(i, ==, 33);
683
684 ops->cleanup(serialize_data);
685 dealloc_helper(&pl, visit_primitive_list, &err);
686 g_assert(!err);
687 dealloc_helper(&pl_copy, visit_primitive_list, &err);
688 g_assert(!err);
689 g_free(args);
690 }
691
692 static void test_struct(gconstpointer opaque)
693 {
694 TestArgs *args = (TestArgs *) opaque;
695 const SerializeOps *ops = args->ops;
696 TestStruct *ts = struct_create();
697 TestStruct *ts_copy = NULL;
698 Error *err = NULL;
699 void *serialize_data;
700
701 ops->serialize(ts, &serialize_data, visit_struct, &err);
702 ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, &err);
703
704 g_assert(err == NULL);
705 struct_compare(ts, ts_copy);
706
707 struct_cleanup(ts);
708 struct_cleanup(ts_copy);
709
710 ops->cleanup(serialize_data);
711 g_free(args);
712 }
713
714 static void test_nested_struct(gconstpointer opaque)
715 {
716 TestArgs *args = (TestArgs *) opaque;
717 const SerializeOps *ops = args->ops;
718 UserDefNested *udnp = nested_struct_create();
719 UserDefNested *udnp_copy = NULL;
720 Error *err = NULL;
721 void *serialize_data;
722
723 ops->serialize(udnp, &serialize_data, visit_nested_struct, &err);
724 ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, &err);
725
726 g_assert(err == NULL);
727 nested_struct_compare(udnp, udnp_copy);
728
729 nested_struct_cleanup(udnp);
730 nested_struct_cleanup(udnp_copy);
731
732 ops->cleanup(serialize_data);
733 g_free(args);
734 }
735
736 static void test_nested_struct_list(gconstpointer opaque)
737 {
738 TestArgs *args = (TestArgs *) opaque;
739 const SerializeOps *ops = args->ops;
740 UserDefNestedList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
741 Error *err = NULL;
742 void *serialize_data;
743 int i = 0;
744
745 for (i = 0; i < 8; i++) {
746 tmp = g_malloc0(sizeof(UserDefNestedList));
747 tmp->value = nested_struct_create();
748 tmp->next = listp;
749 listp = tmp;
750 }
751
752 ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
753 ops->deserialize((void **)&listp_copy, serialize_data,
754 visit_nested_struct_list, &err);
755
756 g_assert(err == NULL);
757
758 tmp = listp;
759 tmp_copy = listp_copy;
760 while (listp_copy) {
761 g_assert(listp);
762 nested_struct_compare(listp->value, listp_copy->value);
763 listp = listp->next;
764 listp_copy = listp_copy->next;
765 }
766
767 qapi_free_UserDefNestedList(tmp);
768 qapi_free_UserDefNestedList(tmp_copy);
769
770 ops->cleanup(serialize_data);
771 g_free(args);
772 }
773
774 static PrimitiveType pt_values[] = {
775 /* string tests */
776 {
777 .description = "string_empty",
778 .type = PTYPE_STRING,
779 .value.string = "",
780 },
781 {
782 .description = "string_whitespace",
783 .type = PTYPE_STRING,
784 .value.string = "a b c\td",
785 },
786 {
787 .description = "string_newlines",
788 .type = PTYPE_STRING,
789 .value.string = "a\nb\n",
790 },
791 {
792 .description = "string_commas",
793 .type = PTYPE_STRING,
794 .value.string = "a,b, c,d",
795 },
796 {
797 .description = "string_single_quoted",
798 .type = PTYPE_STRING,
799 .value.string = "'a b',cd",
800 },
801 {
802 .description = "string_double_quoted",
803 .type = PTYPE_STRING,
804 .value.string = "\"a b\",cd",
805 },
806 /* boolean tests */
807 {
808 .description = "boolean_true1",
809 .type = PTYPE_BOOLEAN,
810 .value.boolean = true,
811 },
812 {
813 .description = "boolean_true2",
814 .type = PTYPE_BOOLEAN,
815 .value.boolean = 8,
816 },
817 {
818 .description = "boolean_true3",
819 .type = PTYPE_BOOLEAN,
820 .value.boolean = -1,
821 },
822 {
823 .description = "boolean_false1",
824 .type = PTYPE_BOOLEAN,
825 .value.boolean = false,
826 },
827 {
828 .description = "boolean_false2",
829 .type = PTYPE_BOOLEAN,
830 .value.boolean = 0,
831 },
832 /* number tests (double) */
833 /* note: we format these to %.6f before comparing, since that's how
834 * we serialize them and it doesn't make sense to check precision
835 * beyond that.
836 */
837 {
838 .description = "number_sanity1",
839 .type = PTYPE_NUMBER,
840 .value.number = -1,
841 },
842 {
843 .description = "number_sanity2",
844 .type = PTYPE_NUMBER,
845 .value.number = 3.14159265,
846 },
847 {
848 .description = "number_min",
849 .type = PTYPE_NUMBER,
850 .value.number = DBL_MIN,
851 },
852 {
853 .description = "number_max",
854 .type = PTYPE_NUMBER,
855 .value.number = DBL_MAX,
856 },
857 /* integer tests (int64) */
858 {
859 .description = "integer_sanity1",
860 .type = PTYPE_INTEGER,
861 .value.integer = -1,
862 },
863 {
864 .description = "integer_sanity2",
865 .type = PTYPE_INTEGER,
866 .value.integer = INT64_MAX / 2 + 1,
867 },
868 {
869 .description = "integer_min",
870 .type = PTYPE_INTEGER,
871 .value.integer = INT64_MIN,
872 },
873 {
874 .description = "integer_max",
875 .type = PTYPE_INTEGER,
876 .value.integer = INT64_MAX,
877 },
878 /* uint8 tests */
879 {
880 .description = "uint8_sanity1",
881 .type = PTYPE_U8,
882 .value.u8 = 1,
883 },
884 {
885 .description = "uint8_sanity2",
886 .type = PTYPE_U8,
887 .value.u8 = UINT8_MAX / 2 + 1,
888 },
889 {
890 .description = "uint8_min",
891 .type = PTYPE_U8,
892 .value.u8 = 0,
893 },
894 {
895 .description = "uint8_max",
896 .type = PTYPE_U8,
897 .value.u8 = UINT8_MAX,
898 },
899 /* uint16 tests */
900 {
901 .description = "uint16_sanity1",
902 .type = PTYPE_U16,
903 .value.u16 = 1,
904 },
905 {
906 .description = "uint16_sanity2",
907 .type = PTYPE_U16,
908 .value.u16 = UINT16_MAX / 2 + 1,
909 },
910 {
911 .description = "uint16_min",
912 .type = PTYPE_U16,
913 .value.u16 = 0,
914 },
915 {
916 .description = "uint16_max",
917 .type = PTYPE_U16,
918 .value.u16 = UINT16_MAX,
919 },
920 /* uint32 tests */
921 {
922 .description = "uint32_sanity1",
923 .type = PTYPE_U32,
924 .value.u32 = 1,
925 },
926 {
927 .description = "uint32_sanity2",
928 .type = PTYPE_U32,
929 .value.u32 = UINT32_MAX / 2 + 1,
930 },
931 {
932 .description = "uint32_min",
933 .type = PTYPE_U32,
934 .value.u32 = 0,
935 },
936 {
937 .description = "uint32_max",
938 .type = PTYPE_U32,
939 .value.u32 = UINT32_MAX,
940 },
941 /* uint64 tests */
942 {
943 .description = "uint64_sanity1",
944 .type = PTYPE_U64,
945 .value.u64 = 1,
946 },
947 {
948 .description = "uint64_sanity2",
949 .type = PTYPE_U64,
950 .value.u64 = UINT64_MAX / 2 + 1,
951 },
952 {
953 .description = "uint64_min",
954 .type = PTYPE_U64,
955 .value.u64 = 0,
956 },
957 {
958 .description = "uint64_max",
959 .type = PTYPE_U64,
960 .value.u64 = UINT64_MAX,
961 },
962 /* int8 tests */
963 {
964 .description = "int8_sanity1",
965 .type = PTYPE_S8,
966 .value.s8 = -1,
967 },
968 {
969 .description = "int8_sanity2",
970 .type = PTYPE_S8,
971 .value.s8 = INT8_MAX / 2 + 1,
972 },
973 {
974 .description = "int8_min",
975 .type = PTYPE_S8,
976 .value.s8 = INT8_MIN,
977 },
978 {
979 .description = "int8_max",
980 .type = PTYPE_S8,
981 .value.s8 = INT8_MAX,
982 },
983 /* int16 tests */
984 {
985 .description = "int16_sanity1",
986 .type = PTYPE_S16,
987 .value.s16 = -1,
988 },
989 {
990 .description = "int16_sanity2",
991 .type = PTYPE_S16,
992 .value.s16 = INT16_MAX / 2 + 1,
993 },
994 {
995 .description = "int16_min",
996 .type = PTYPE_S16,
997 .value.s16 = INT16_MIN,
998 },
999 {
1000 .description = "int16_max",
1001 .type = PTYPE_S16,
1002 .value.s16 = INT16_MAX,
1003 },
1004 /* int32 tests */
1005 {
1006 .description = "int32_sanity1",
1007 .type = PTYPE_S32,
1008 .value.s32 = -1,
1009 },
1010 {
1011 .description = "int32_sanity2",
1012 .type = PTYPE_S32,
1013 .value.s32 = INT32_MAX / 2 + 1,
1014 },
1015 {
1016 .description = "int32_min",
1017 .type = PTYPE_S32,
1018 .value.s32 = INT32_MIN,
1019 },
1020 {
1021 .description = "int32_max",
1022 .type = PTYPE_S32,
1023 .value.s32 = INT32_MAX,
1024 },
1025 /* int64 tests */
1026 {
1027 .description = "int64_sanity1",
1028 .type = PTYPE_S64,
1029 .value.s64 = -1,
1030 },
1031 {
1032 .description = "int64_sanity2",
1033 .type = PTYPE_S64,
1034 .value.s64 = INT64_MAX / 2 + 1,
1035 },
1036 {
1037 .description = "int64_min",
1038 .type = PTYPE_S64,
1039 .value.s64 = INT64_MIN,
1040 },
1041 {
1042 .description = "int64_max",
1043 .type = PTYPE_S64,
1044 .value.s64 = INT64_MAX,
1045 },
1046 { .type = PTYPE_EOL }
1047 };
1048
1049 /* visitor-specific op implementations */
1050
1051 typedef struct QmpSerializeData {
1052 QmpOutputVisitor *qov;
1053 QmpInputVisitor *qiv;
1054 } QmpSerializeData;
1055
1056 static void qmp_serialize(void *native_in, void **datap,
1057 VisitorFunc visit, Error **errp)
1058 {
1059 QmpSerializeData *d = g_malloc0(sizeof(*d));
1060
1061 d->qov = qmp_output_visitor_new();
1062 visit(qmp_output_get_visitor(d->qov), &native_in, errp);
1063 *datap = d;
1064 }
1065
1066 static void qmp_deserialize(void **native_out, void *datap,
1067 VisitorFunc visit, Error **errp)
1068 {
1069 QmpSerializeData *d = datap;
1070 QString *output_json;
1071 QObject *obj_orig, *obj;
1072
1073 obj_orig = qmp_output_get_qobject(d->qov);
1074 output_json = qobject_to_json(obj_orig);
1075 obj = qobject_from_json(qstring_get_str(output_json));
1076
1077 QDECREF(output_json);
1078 d->qiv = qmp_input_visitor_new(obj);
1079 qobject_decref(obj_orig);
1080 qobject_decref(obj);
1081 visit(qmp_input_get_visitor(d->qiv), native_out, errp);
1082 }
1083
1084 static void qmp_cleanup(void *datap)
1085 {
1086 QmpSerializeData *d = datap;
1087 qmp_output_visitor_cleanup(d->qov);
1088 qmp_input_visitor_cleanup(d->qiv);
1089
1090 g_free(d);
1091 }
1092
1093 typedef struct StringSerializeData {
1094 char *string;
1095 StringOutputVisitor *sov;
1096 StringInputVisitor *siv;
1097 } StringSerializeData;
1098
1099 static void string_serialize(void *native_in, void **datap,
1100 VisitorFunc visit, Error **errp)
1101 {
1102 StringSerializeData *d = g_malloc0(sizeof(*d));
1103
1104 d->sov = string_output_visitor_new(false);
1105 visit(string_output_get_visitor(d->sov), &native_in, errp);
1106 *datap = d;
1107 }
1108
1109 static void string_deserialize(void **native_out, void *datap,
1110 VisitorFunc visit, Error **errp)
1111 {
1112 StringSerializeData *d = datap;
1113
1114 d->string = string_output_get_string(d->sov);
1115 d->siv = string_input_visitor_new(d->string);
1116 visit(string_input_get_visitor(d->siv), native_out, errp);
1117 }
1118
1119 static void string_cleanup(void *datap)
1120 {
1121 StringSerializeData *d = datap;
1122
1123 string_output_visitor_cleanup(d->sov);
1124 string_input_visitor_cleanup(d->siv);
1125 g_free(d->string);
1126 g_free(d);
1127 }
1128
1129 /* visitor registration, test harness */
1130
1131 /* note: to function interchangeably as a serialization mechanism your
1132 * visitor test implementation should pass the test cases for all visitor
1133 * capabilities: primitives, structures, and lists
1134 */
1135 static const SerializeOps visitors[] = {
1136 {
1137 .type = "QMP",
1138 .serialize = qmp_serialize,
1139 .deserialize = qmp_deserialize,
1140 .cleanup = qmp_cleanup,
1141 .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS |
1142 VCAP_PRIMITIVE_LISTS
1143 },
1144 {
1145 .type = "String",
1146 .serialize = string_serialize,
1147 .deserialize = string_deserialize,
1148 .cleanup = string_cleanup,
1149 .caps = VCAP_PRIMITIVES
1150 },
1151 { NULL }
1152 };
1153
1154 static void add_visitor_type(const SerializeOps *ops)
1155 {
1156 char testname_prefix[128];
1157 char testname[128];
1158 TestArgs *args;
1159 int i = 0;
1160
1161 sprintf(testname_prefix, "/visitor/serialization/%s", ops->type);
1162
1163 if (ops->caps & VCAP_PRIMITIVES) {
1164 while (pt_values[i].type != PTYPE_EOL) {
1165 sprintf(testname, "%s/primitives/%s", testname_prefix,
1166 pt_values[i].description);
1167 args = g_malloc0(sizeof(*args));
1168 args->ops = ops;
1169 args->test_data = &pt_values[i];
1170 g_test_add_data_func(testname, args, test_primitives);
1171 i++;
1172 }
1173 }
1174
1175 if (ops->caps & VCAP_STRUCTURES) {
1176 sprintf(testname, "%s/struct", testname_prefix);
1177 args = g_malloc0(sizeof(*args));
1178 args->ops = ops;
1179 args->test_data = NULL;
1180 g_test_add_data_func(testname, args, test_struct);
1181
1182 sprintf(testname, "%s/nested_struct", testname_prefix);
1183 args = g_malloc0(sizeof(*args));
1184 args->ops = ops;
1185 args->test_data = NULL;
1186 g_test_add_data_func(testname, args, test_nested_struct);
1187 }
1188
1189 if (ops->caps & VCAP_LISTS) {
1190 sprintf(testname, "%s/nested_struct_list", testname_prefix);
1191 args = g_malloc0(sizeof(*args));
1192 args->ops = ops;
1193 args->test_data = NULL;
1194 g_test_add_data_func(testname, args, test_nested_struct_list);
1195 }
1196
1197 if (ops->caps & VCAP_PRIMITIVE_LISTS) {
1198 i = 0;
1199 while (pt_values[i].type != PTYPE_EOL) {
1200 sprintf(testname, "%s/primitive_list/%s", testname_prefix,
1201 pt_values[i].description);
1202 args = g_malloc0(sizeof(*args));
1203 args->ops = ops;
1204 args->test_data = &pt_values[i];
1205 g_test_add_data_func(testname, args, test_primitive_lists);
1206 i++;
1207 }
1208 }
1209 }
1210
1211 int main(int argc, char **argv)
1212 {
1213 int i = 0;
1214
1215 g_test_init(&argc, &argv, NULL);
1216
1217 while (visitors[i].type != NULL) {
1218 add_visitor_type(&visitors[i]);
1219 i++;
1220 }
1221
1222 g_test_run();
1223
1224 return 0;
1225 }