xref: /qemu/tests/unit/test-visitor-serialization.c (revision ad7f375df681503baa6ebef065818868e1216976)
12d496105SMichael Roth /*
22d496105SMichael Roth  * Unit-tests for visitor-based serialization
32d496105SMichael Roth  *
42d496105SMichael Roth  * Copyright IBM, Corp. 2012
52d496105SMichael Roth  *
62d496105SMichael Roth  * Authors:
72d496105SMichael Roth  *  Michael Roth <mdroth@linux.vnet.ibm.com>
82d496105SMichael Roth  *
92d496105SMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
102d496105SMichael Roth  * See the COPYING file in the top-level directory.
112d496105SMichael Roth  */
122d496105SMichael Roth 
132d496105SMichael Roth #include <glib.h>
142d496105SMichael Roth #include <stdlib.h>
152d496105SMichael Roth #include <stdint.h>
162d496105SMichael Roth #include <float.h>
1779ee7df8SPaolo Bonzini 
1879ee7df8SPaolo Bonzini #include "qemu-common.h"
192d496105SMichael Roth #include "test-qapi-types.h"
202d496105SMichael Roth #include "test-qapi-visit.h"
217b1b5d19SPaolo Bonzini #include "qapi/qmp/types.h"
222d496105SMichael Roth #include "qapi/qmp-input-visitor.h"
232d496105SMichael Roth #include "qapi/qmp-output-visitor.h"
240d30b0a2SMichael Roth #include "qapi/string-input-visitor.h"
250d30b0a2SMichael Roth #include "qapi/string-output-visitor.h"
262d496105SMichael Roth 
272d496105SMichael Roth typedef struct PrimitiveType {
282d496105SMichael Roth     union {
292d496105SMichael Roth         const char *string;
302d496105SMichael Roth         bool boolean;
312d496105SMichael Roth         double number;
322d496105SMichael Roth         int64_t integer;
332d496105SMichael Roth         uint8_t u8;
342d496105SMichael Roth         uint16_t u16;
352d496105SMichael Roth         uint32_t u32;
362d496105SMichael Roth         uint64_t u64;
372d496105SMichael Roth         int8_t s8;
382d496105SMichael Roth         int16_t s16;
392d496105SMichael Roth         int32_t s32;
402d496105SMichael Roth         int64_t s64;
412d496105SMichael Roth         intmax_t max;
422d496105SMichael Roth     } value;
432d496105SMichael Roth     enum {
442d496105SMichael Roth         PTYPE_STRING = 0,
452d496105SMichael Roth         PTYPE_BOOLEAN,
462d496105SMichael Roth         PTYPE_NUMBER,
472d496105SMichael Roth         PTYPE_INTEGER,
482d496105SMichael Roth         PTYPE_U8,
492d496105SMichael Roth         PTYPE_U16,
502d496105SMichael Roth         PTYPE_U32,
512d496105SMichael Roth         PTYPE_U64,
522d496105SMichael Roth         PTYPE_S8,
532d496105SMichael Roth         PTYPE_S16,
542d496105SMichael Roth         PTYPE_S32,
552d496105SMichael Roth         PTYPE_S64,
562d496105SMichael Roth         PTYPE_EOL,
572d496105SMichael Roth     } type;
582d496105SMichael Roth     const char *description;
592d496105SMichael Roth } PrimitiveType;
602d496105SMichael Roth 
612d496105SMichael Roth /* test helpers */
622d496105SMichael Roth 
632d496105SMichael Roth static void visit_primitive_type(Visitor *v, void **native, Error **errp)
642d496105SMichael Roth {
652d496105SMichael Roth     PrimitiveType *pt = *native;
662d496105SMichael Roth     switch(pt->type) {
672d496105SMichael Roth     case PTYPE_STRING:
682d496105SMichael Roth         visit_type_str(v, (char **)&pt->value.string, NULL, errp);
692d496105SMichael Roth         break;
702d496105SMichael Roth     case PTYPE_BOOLEAN:
712d496105SMichael Roth         visit_type_bool(v, &pt->value.boolean, NULL, errp);
722d496105SMichael Roth         break;
732d496105SMichael Roth     case PTYPE_NUMBER:
742d496105SMichael Roth         visit_type_number(v, &pt->value.number, NULL, errp);
752d496105SMichael Roth         break;
762d496105SMichael Roth     case PTYPE_INTEGER:
772d496105SMichael Roth         visit_type_int(v, &pt->value.integer, NULL, errp);
782d496105SMichael Roth         break;
792d496105SMichael Roth     case PTYPE_U8:
802d496105SMichael Roth         visit_type_uint8(v, &pt->value.u8, NULL, errp);
812d496105SMichael Roth         break;
822d496105SMichael Roth     case PTYPE_U16:
832d496105SMichael Roth         visit_type_uint16(v, &pt->value.u16, NULL, errp);
842d496105SMichael Roth         break;
852d496105SMichael Roth     case PTYPE_U32:
862d496105SMichael Roth         visit_type_uint32(v, &pt->value.u32, NULL, errp);
872d496105SMichael Roth         break;
882d496105SMichael Roth     case PTYPE_U64:
892d496105SMichael Roth         visit_type_uint64(v, &pt->value.u64, NULL, errp);
902d496105SMichael Roth         break;
912d496105SMichael Roth     case PTYPE_S8:
922d496105SMichael Roth         visit_type_int8(v, &pt->value.s8, NULL, errp);
932d496105SMichael Roth         break;
942d496105SMichael Roth     case PTYPE_S16:
952d496105SMichael Roth         visit_type_int16(v, &pt->value.s16, NULL, errp);
962d496105SMichael Roth         break;
972d496105SMichael Roth     case PTYPE_S32:
982d496105SMichael Roth         visit_type_int32(v, &pt->value.s32, NULL, errp);
992d496105SMichael Roth         break;
1002d496105SMichael Roth     case PTYPE_S64:
1012d496105SMichael Roth         visit_type_int64(v, &pt->value.s64, NULL, errp);
1022d496105SMichael Roth         break;
1032d496105SMichael Roth     case PTYPE_EOL:
1042d496105SMichael Roth         g_assert(false);
1052d496105SMichael Roth     }
1062d496105SMichael Roth }
1072d496105SMichael Roth 
1082d496105SMichael Roth typedef struct TestStruct
1092d496105SMichael Roth {
1102d496105SMichael Roth     int64_t integer;
1112d496105SMichael Roth     bool boolean;
1122d496105SMichael Roth     char *string;
1132d496105SMichael Roth } TestStruct;
1142d496105SMichael Roth 
1152d496105SMichael Roth static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
1162d496105SMichael Roth                                   const char *name, Error **errp)
1172d496105SMichael Roth {
1182d496105SMichael Roth     visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), errp);
1192d496105SMichael Roth 
1202d496105SMichael Roth     visit_type_int(v, &(*obj)->integer, "integer", errp);
1212d496105SMichael Roth     visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
1222d496105SMichael Roth     visit_type_str(v, &(*obj)->string, "string", errp);
1232d496105SMichael Roth 
1242d496105SMichael Roth     visit_end_struct(v, errp);
1252d496105SMichael Roth }
1262d496105SMichael Roth 
1272d496105SMichael Roth static TestStruct *struct_create(void)
1282d496105SMichael Roth {
1292d496105SMichael Roth     TestStruct *ts = g_malloc0(sizeof(*ts));
1302d496105SMichael Roth     ts->integer = -42;
1312d496105SMichael Roth     ts->boolean = true;
1322d496105SMichael Roth     ts->string = strdup("test string");
1332d496105SMichael Roth     return ts;
1342d496105SMichael Roth }
1352d496105SMichael Roth 
1362d496105SMichael Roth static void struct_compare(TestStruct *ts1, TestStruct *ts2)
1372d496105SMichael Roth {
1382d496105SMichael Roth     g_assert(ts1);
1392d496105SMichael Roth     g_assert(ts2);
1402d496105SMichael Roth     g_assert_cmpint(ts1->integer, ==, ts2->integer);
1412d496105SMichael Roth     g_assert(ts1->boolean == ts2->boolean);
1422d496105SMichael Roth     g_assert_cmpstr(ts1->string, ==, ts2->string);
1432d496105SMichael Roth }
1442d496105SMichael Roth 
1452d496105SMichael Roth static void struct_cleanup(TestStruct *ts)
1462d496105SMichael Roth {
1472d496105SMichael Roth     g_free(ts->string);
1482d496105SMichael Roth     g_free(ts);
1492d496105SMichael Roth }
1502d496105SMichael Roth 
1512d496105SMichael Roth static void visit_struct(Visitor *v, void **native, Error **errp)
1522d496105SMichael Roth {
1532d496105SMichael Roth     visit_type_TestStruct(v, (TestStruct **)native, NULL, errp);
1542d496105SMichael Roth }
1552d496105SMichael Roth 
1562d496105SMichael Roth static UserDefNested *nested_struct_create(void)
1572d496105SMichael Roth {
1582d496105SMichael Roth     UserDefNested *udnp = g_malloc0(sizeof(*udnp));
1592d496105SMichael Roth     udnp->string0 = strdup("test_string0");
1602d496105SMichael Roth     udnp->dict1.string1 = strdup("test_string1");
1612d496105SMichael Roth     udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
1622d496105SMichael Roth     udnp->dict1.dict2.userdef1->integer = 42;
1632d496105SMichael Roth     udnp->dict1.dict2.userdef1->string = strdup("test_string");
1642d496105SMichael Roth     udnp->dict1.dict2.string2 = strdup("test_string2");
1652d496105SMichael Roth     udnp->dict1.has_dict3 = true;
1662d496105SMichael Roth     udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
1672d496105SMichael Roth     udnp->dict1.dict3.userdef2->integer = 43;
1682d496105SMichael Roth     udnp->dict1.dict3.userdef2->string = strdup("test_string");
1692d496105SMichael Roth     udnp->dict1.dict3.string3 = strdup("test_string3");
1702d496105SMichael Roth     return udnp;
1712d496105SMichael Roth }
1722d496105SMichael Roth 
1732d496105SMichael Roth static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2)
1742d496105SMichael Roth {
1752d496105SMichael Roth     g_assert(udnp1);
1762d496105SMichael Roth     g_assert(udnp2);
1772d496105SMichael Roth     g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
1782d496105SMichael Roth     g_assert_cmpstr(udnp1->dict1.string1, ==, udnp2->dict1.string1);
1792d496105SMichael Roth     g_assert_cmpint(udnp1->dict1.dict2.userdef1->integer, ==,
1802d496105SMichael Roth                     udnp2->dict1.dict2.userdef1->integer);
1812d496105SMichael Roth     g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==,
1822d496105SMichael Roth                     udnp2->dict1.dict2.userdef1->string);
1832d496105SMichael Roth     g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2);
1842d496105SMichael Roth     g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3);
1852d496105SMichael Roth     g_assert_cmpint(udnp1->dict1.dict3.userdef2->integer, ==,
1862d496105SMichael Roth                     udnp2->dict1.dict3.userdef2->integer);
1872d496105SMichael Roth     g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==,
1882d496105SMichael Roth                     udnp2->dict1.dict3.userdef2->string);
1892d496105SMichael Roth     g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3);
1902d496105SMichael Roth }
1912d496105SMichael Roth 
1922d496105SMichael Roth static void nested_struct_cleanup(UserDefNested *udnp)
1932d496105SMichael Roth {
1942d496105SMichael Roth     qapi_free_UserDefNested(udnp);
1952d496105SMichael Roth }
1962d496105SMichael Roth 
1972d496105SMichael Roth static void visit_nested_struct(Visitor *v, void **native, Error **errp)
1982d496105SMichael Roth {
1992d496105SMichael Roth     visit_type_UserDefNested(v, (UserDefNested **)native, NULL, errp);
2002d496105SMichael Roth }
2012d496105SMichael Roth 
2022d496105SMichael Roth static void visit_nested_struct_list(Visitor *v, void **native, Error **errp)
2032d496105SMichael Roth {
2042d496105SMichael Roth     visit_type_UserDefNestedList(v, (UserDefNestedList **)native, NULL, errp);
2052d496105SMichael Roth }
2062d496105SMichael Roth 
2072d496105SMichael Roth /* test cases */
2082d496105SMichael Roth 
2092d496105SMichael Roth typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp);
2102d496105SMichael Roth 
2112d496105SMichael Roth typedef enum VisitorCapabilities {
2122d496105SMichael Roth     VCAP_PRIMITIVES = 1,
2132d496105SMichael Roth     VCAP_STRUCTURES = 2,
2142d496105SMichael Roth     VCAP_LISTS = 4,
2152d496105SMichael Roth } VisitorCapabilities;
2162d496105SMichael Roth 
2172d496105SMichael Roth typedef struct SerializeOps {
2182d496105SMichael Roth     void (*serialize)(void *native_in, void **datap,
2192d496105SMichael Roth                       VisitorFunc visit, Error **errp);
2202d496105SMichael Roth     void (*deserialize)(void **native_out, void *datap,
2212d496105SMichael Roth                             VisitorFunc visit, Error **errp);
2222d496105SMichael Roth     void (*cleanup)(void *datap);
2232d496105SMichael Roth     const char *type;
2242d496105SMichael Roth     VisitorCapabilities caps;
2252d496105SMichael Roth } SerializeOps;
2262d496105SMichael Roth 
2272d496105SMichael Roth typedef struct TestArgs {
2282d496105SMichael Roth     const SerializeOps *ops;
2292d496105SMichael Roth     void *test_data;
2302d496105SMichael Roth } TestArgs;
2312d496105SMichael Roth 
2322d496105SMichael Roth #define FLOAT_STRING_PRECISION 6 /* corresponding to n in %.nf formatting */
2332d496105SMichael Roth static gsize calc_float_string_storage(double value)
2342d496105SMichael Roth {
2352d496105SMichael Roth     int whole_value = value;
2362d496105SMichael Roth     gsize i = 0;
2372d496105SMichael Roth     do {
2382d496105SMichael Roth         i++;
2392d496105SMichael Roth     } while (whole_value /= 10);
2402d496105SMichael Roth     return i + 2 + FLOAT_STRING_PRECISION;
2412d496105SMichael Roth }
2422d496105SMichael Roth 
2432d496105SMichael Roth static void test_primitives(gconstpointer opaque)
2442d496105SMichael Roth {
2452d496105SMichael Roth     TestArgs *args = (TestArgs *) opaque;
2462d496105SMichael Roth     const SerializeOps *ops = args->ops;
2472d496105SMichael Roth     PrimitiveType *pt = args->test_data;
2482d496105SMichael Roth     PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy));
2492d496105SMichael Roth     Error *err = NULL;
2502d496105SMichael Roth     void *serialize_data;
2512d496105SMichael Roth     char *double1, *double2;
2522d496105SMichael Roth 
2532d496105SMichael Roth     pt_copy->type = pt->type;
2542d496105SMichael Roth     ops->serialize(pt, &serialize_data, visit_primitive_type, &err);
2552d496105SMichael Roth     ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, &err);
2562d496105SMichael Roth 
2572d496105SMichael Roth     g_assert(err == NULL);
2582d496105SMichael Roth     g_assert(pt_copy != NULL);
2592d496105SMichael Roth     if (pt->type == PTYPE_STRING) {
2602d496105SMichael Roth         g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string);
2612bd01ac1SStefan Berger         g_free((char *)pt_copy->value.string);
2622d496105SMichael Roth     } else if (pt->type == PTYPE_NUMBER) {
2632d496105SMichael Roth         /* we serialize with %f for our reference visitors, so rather than fuzzy
2642d496105SMichael Roth          * floating math to test "equality", just compare the formatted values
2652d496105SMichael Roth          */
2662d496105SMichael Roth         double1 = g_malloc0(calc_float_string_storage(pt->value.number));
2672d496105SMichael Roth         double2 = g_malloc0(calc_float_string_storage(pt_copy->value.number));
2682d496105SMichael Roth         g_assert_cmpstr(double1, ==, double2);
2692d496105SMichael Roth         g_free(double1);
2702d496105SMichael Roth         g_free(double2);
2712d496105SMichael Roth     } else if (pt->type == PTYPE_BOOLEAN) {
2722d496105SMichael Roth         g_assert_cmpint(!!pt->value.max, ==, !!pt->value.max);
2732d496105SMichael Roth     } else {
2742d496105SMichael Roth         g_assert_cmpint(pt->value.max, ==, pt_copy->value.max);
2752d496105SMichael Roth     }
2762d496105SMichael Roth 
2772d496105SMichael Roth     ops->cleanup(serialize_data);
2782d496105SMichael Roth     g_free(args);
2792bd01ac1SStefan Berger     g_free(pt_copy);
2802d496105SMichael Roth }
2812d496105SMichael Roth 
2822d496105SMichael Roth static void test_struct(gconstpointer opaque)
2832d496105SMichael Roth {
2842d496105SMichael Roth     TestArgs *args = (TestArgs *) opaque;
2852d496105SMichael Roth     const SerializeOps *ops = args->ops;
2862d496105SMichael Roth     TestStruct *ts = struct_create();
2872d496105SMichael Roth     TestStruct *ts_copy = NULL;
2882d496105SMichael Roth     Error *err = NULL;
2892d496105SMichael Roth     void *serialize_data;
2902d496105SMichael Roth 
2912d496105SMichael Roth     ops->serialize(ts, &serialize_data, visit_struct, &err);
2922d496105SMichael Roth     ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, &err);
2932d496105SMichael Roth 
2942d496105SMichael Roth     g_assert(err == NULL);
2952d496105SMichael Roth     struct_compare(ts, ts_copy);
2962d496105SMichael Roth 
2972d496105SMichael Roth     struct_cleanup(ts);
2982d496105SMichael Roth     struct_cleanup(ts_copy);
2992d496105SMichael Roth 
3002d496105SMichael Roth     ops->cleanup(serialize_data);
3012d496105SMichael Roth     g_free(args);
3022d496105SMichael Roth }
3032d496105SMichael Roth 
3042d496105SMichael Roth static void test_nested_struct(gconstpointer opaque)
3052d496105SMichael Roth {
3062d496105SMichael Roth     TestArgs *args = (TestArgs *) opaque;
3072d496105SMichael Roth     const SerializeOps *ops = args->ops;
3082d496105SMichael Roth     UserDefNested *udnp = nested_struct_create();
3092d496105SMichael Roth     UserDefNested *udnp_copy = NULL;
3102d496105SMichael Roth     Error *err = NULL;
3112d496105SMichael Roth     void *serialize_data;
3122d496105SMichael Roth 
3132d496105SMichael Roth     ops->serialize(udnp, &serialize_data, visit_nested_struct, &err);
3142d496105SMichael Roth     ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, &err);
3152d496105SMichael Roth 
3162d496105SMichael Roth     g_assert(err == NULL);
3172d496105SMichael Roth     nested_struct_compare(udnp, udnp_copy);
3182d496105SMichael Roth 
3192d496105SMichael Roth     nested_struct_cleanup(udnp);
3202d496105SMichael Roth     nested_struct_cleanup(udnp_copy);
3212d496105SMichael Roth 
3222d496105SMichael Roth     ops->cleanup(serialize_data);
3232d496105SMichael Roth     g_free(args);
3242d496105SMichael Roth }
3252d496105SMichael Roth 
3262d496105SMichael Roth static void test_nested_struct_list(gconstpointer opaque)
3272d496105SMichael Roth {
3282d496105SMichael Roth     TestArgs *args = (TestArgs *) opaque;
3292d496105SMichael Roth     const SerializeOps *ops = args->ops;
3302d496105SMichael Roth     UserDefNestedList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
3312d496105SMichael Roth     Error *err = NULL;
3322d496105SMichael Roth     void *serialize_data;
3332d496105SMichael Roth     int i = 0;
3342d496105SMichael Roth 
3352d496105SMichael Roth     for (i = 0; i < 8; i++) {
3362d496105SMichael Roth         tmp = g_malloc0(sizeof(UserDefNestedList));
3372d496105SMichael Roth         tmp->value = nested_struct_create();
3382d496105SMichael Roth         tmp->next = listp;
3392d496105SMichael Roth         listp = tmp;
3402d496105SMichael Roth     }
3412d496105SMichael Roth 
3422d496105SMichael Roth     ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
3432d496105SMichael Roth     ops->deserialize((void **)&listp_copy, serialize_data,
3442d496105SMichael Roth                      visit_nested_struct_list, &err);
3452d496105SMichael Roth 
3462d496105SMichael Roth     g_assert(err == NULL);
3472d496105SMichael Roth 
3482d496105SMichael Roth     tmp = listp;
3492d496105SMichael Roth     tmp_copy = listp_copy;
3502d496105SMichael Roth     while (listp_copy) {
3512d496105SMichael Roth         g_assert(listp);
3522d496105SMichael Roth         nested_struct_compare(listp->value, listp_copy->value);
3532d496105SMichael Roth         listp = listp->next;
3542d496105SMichael Roth         listp_copy = listp_copy->next;
3552d496105SMichael Roth     }
3562d496105SMichael Roth 
3572d496105SMichael Roth     qapi_free_UserDefNestedList(tmp);
3582d496105SMichael Roth     qapi_free_UserDefNestedList(tmp_copy);
3592d496105SMichael Roth 
3602d496105SMichael Roth     ops->cleanup(serialize_data);
3612d496105SMichael Roth     g_free(args);
3622d496105SMichael Roth }
3632d496105SMichael Roth 
3642d496105SMichael Roth PrimitiveType pt_values[] = {
3652d496105SMichael Roth     /* string tests */
3662d496105SMichael Roth     {
3672d496105SMichael Roth         .description = "string_empty",
3682d496105SMichael Roth         .type = PTYPE_STRING,
3692d496105SMichael Roth         .value.string = "",
3702d496105SMichael Roth     },
3712d496105SMichael Roth     {
3722d496105SMichael Roth         .description = "string_whitespace",
3732d496105SMichael Roth         .type = PTYPE_STRING,
3742d496105SMichael Roth         .value.string = "a b  c\td",
3752d496105SMichael Roth     },
3762d496105SMichael Roth     {
3772d496105SMichael Roth         .description = "string_newlines",
3782d496105SMichael Roth         .type = PTYPE_STRING,
3792d496105SMichael Roth         .value.string = "a\nb\n",
3802d496105SMichael Roth     },
3812d496105SMichael Roth     {
3822d496105SMichael Roth         .description = "string_commas",
3832d496105SMichael Roth         .type = PTYPE_STRING,
3842d496105SMichael Roth         .value.string = "a,b, c,d",
3852d496105SMichael Roth     },
3862d496105SMichael Roth     {
3872d496105SMichael Roth         .description = "string_single_quoted",
3882d496105SMichael Roth         .type = PTYPE_STRING,
3892d496105SMichael Roth         .value.string = "'a b',cd",
3902d496105SMichael Roth     },
3912d496105SMichael Roth     {
3922d496105SMichael Roth         .description = "string_double_quoted",
3932d496105SMichael Roth         .type = PTYPE_STRING,
3942d496105SMichael Roth         .value.string = "\"a b\",cd",
3952d496105SMichael Roth     },
3962d496105SMichael Roth     /* boolean tests */
3972d496105SMichael Roth     {
3982d496105SMichael Roth         .description = "boolean_true1",
3992d496105SMichael Roth         .type = PTYPE_BOOLEAN,
4002d496105SMichael Roth         .value.boolean = true,
4012d496105SMichael Roth     },
4022d496105SMichael Roth     {
4032d496105SMichael Roth         .description = "boolean_true2",
4042d496105SMichael Roth         .type = PTYPE_BOOLEAN,
4052d496105SMichael Roth         .value.boolean = 8,
4062d496105SMichael Roth     },
4072d496105SMichael Roth     {
4082d496105SMichael Roth         .description = "boolean_true3",
4092d496105SMichael Roth         .type = PTYPE_BOOLEAN,
4102d496105SMichael Roth         .value.boolean = -1,
4112d496105SMichael Roth     },
4122d496105SMichael Roth     {
4132d496105SMichael Roth         .description = "boolean_false1",
4142d496105SMichael Roth         .type = PTYPE_BOOLEAN,
4152d496105SMichael Roth         .value.boolean = false,
4162d496105SMichael Roth     },
4172d496105SMichael Roth     {
4182d496105SMichael Roth         .description = "boolean_false2",
4192d496105SMichael Roth         .type = PTYPE_BOOLEAN,
4202d496105SMichael Roth         .value.boolean = 0,
4212d496105SMichael Roth     },
4222d496105SMichael Roth     /* number tests (double) */
4232d496105SMichael Roth     /* note: we format these to %.6f before comparing, since that's how
4242d496105SMichael Roth      * we serialize them and it doesn't make sense to check precision
4252d496105SMichael Roth      * beyond that.
4262d496105SMichael Roth      */
4272d496105SMichael Roth     {
4282d496105SMichael Roth         .description = "number_sanity1",
4292d496105SMichael Roth         .type = PTYPE_NUMBER,
4302d496105SMichael Roth         .value.number = -1,
4312d496105SMichael Roth     },
4322d496105SMichael Roth     {
4332d496105SMichael Roth         .description = "number_sanity2",
4342d496105SMichael Roth         .type = PTYPE_NUMBER,
4352d496105SMichael Roth         .value.number = 3.14159265,
4362d496105SMichael Roth     },
4372d496105SMichael Roth     {
4382d496105SMichael Roth         .description = "number_min",
4392d496105SMichael Roth         .type = PTYPE_NUMBER,
4402d496105SMichael Roth         .value.number = DBL_MIN,
4412d496105SMichael Roth     },
4422d496105SMichael Roth     {
4432d496105SMichael Roth         .description = "number_max",
4442d496105SMichael Roth         .type = PTYPE_NUMBER,
4452d496105SMichael Roth         .value.number = DBL_MAX,
4462d496105SMichael Roth     },
4472d496105SMichael Roth     /* integer tests (int64) */
4482d496105SMichael Roth     {
4492d496105SMichael Roth         .description = "integer_sanity1",
4502d496105SMichael Roth         .type = PTYPE_INTEGER,
4512d496105SMichael Roth         .value.integer = -1,
4522d496105SMichael Roth     },
4532d496105SMichael Roth     {
4542d496105SMichael Roth         .description = "integer_sanity2",
4552d496105SMichael Roth         .type = PTYPE_INTEGER,
4562d496105SMichael Roth         .value.integer = INT64_MAX / 2 + 1,
4572d496105SMichael Roth     },
4582d496105SMichael Roth     {
4592d496105SMichael Roth         .description = "integer_min",
4602d496105SMichael Roth         .type = PTYPE_INTEGER,
4612d496105SMichael Roth         .value.integer = INT64_MIN,
4622d496105SMichael Roth     },
4632d496105SMichael Roth     {
4642d496105SMichael Roth         .description = "integer_max",
4652d496105SMichael Roth         .type = PTYPE_INTEGER,
4662d496105SMichael Roth         .value.integer = INT64_MAX,
4672d496105SMichael Roth     },
4682d496105SMichael Roth     /* uint8 tests */
4692d496105SMichael Roth     {
4702d496105SMichael Roth         .description = "uint8_sanity1",
4712d496105SMichael Roth         .type = PTYPE_U8,
4722d496105SMichael Roth         .value.u8 = 1,
4732d496105SMichael Roth     },
4742d496105SMichael Roth     {
4752d496105SMichael Roth         .description = "uint8_sanity2",
4762d496105SMichael Roth         .type = PTYPE_U8,
4772d496105SMichael Roth         .value.u8 = UINT8_MAX / 2 + 1,
4782d496105SMichael Roth     },
4792d496105SMichael Roth     {
4802d496105SMichael Roth         .description = "uint8_min",
4812d496105SMichael Roth         .type = PTYPE_U8,
4822d496105SMichael Roth         .value.u8 = 0,
4832d496105SMichael Roth     },
4842d496105SMichael Roth     {
4852d496105SMichael Roth         .description = "uint8_max",
4862d496105SMichael Roth         .type = PTYPE_U8,
4872d496105SMichael Roth         .value.u8 = UINT8_MAX,
4882d496105SMichael Roth     },
4892d496105SMichael Roth     /* uint16 tests */
4902d496105SMichael Roth     {
4912d496105SMichael Roth         .description = "uint16_sanity1",
4922d496105SMichael Roth         .type = PTYPE_U16,
4932d496105SMichael Roth         .value.u16 = 1,
4942d496105SMichael Roth     },
4952d496105SMichael Roth     {
4962d496105SMichael Roth         .description = "uint16_sanity2",
4972d496105SMichael Roth         .type = PTYPE_U16,
4982d496105SMichael Roth         .value.u16 = UINT16_MAX / 2 + 1,
4992d496105SMichael Roth     },
5002d496105SMichael Roth     {
5012d496105SMichael Roth         .description = "uint16_min",
5022d496105SMichael Roth         .type = PTYPE_U16,
5032d496105SMichael Roth         .value.u16 = 0,
5042d496105SMichael Roth     },
5052d496105SMichael Roth     {
5062d496105SMichael Roth         .description = "uint16_max",
5072d496105SMichael Roth         .type = PTYPE_U16,
5082d496105SMichael Roth         .value.u16 = UINT16_MAX,
5092d496105SMichael Roth     },
5102d496105SMichael Roth     /* uint32 tests */
5112d496105SMichael Roth     {
5122d496105SMichael Roth         .description = "uint32_sanity1",
5132d496105SMichael Roth         .type = PTYPE_U32,
5142d496105SMichael Roth         .value.u32 = 1,
5152d496105SMichael Roth     },
5162d496105SMichael Roth     {
5172d496105SMichael Roth         .description = "uint32_sanity2",
5182d496105SMichael Roth         .type = PTYPE_U32,
5192d496105SMichael Roth         .value.u32 = UINT32_MAX / 2 + 1,
5202d496105SMichael Roth     },
5212d496105SMichael Roth     {
5222d496105SMichael Roth         .description = "uint32_min",
5232d496105SMichael Roth         .type = PTYPE_U32,
5242d496105SMichael Roth         .value.u32 = 0,
5252d496105SMichael Roth     },
5262d496105SMichael Roth     {
5272d496105SMichael Roth         .description = "uint32_max",
5282d496105SMichael Roth         .type = PTYPE_U32,
5292d496105SMichael Roth         .value.u32 = UINT32_MAX,
5302d496105SMichael Roth     },
5312d496105SMichael Roth     /* uint64 tests */
5322d496105SMichael Roth     {
5332d496105SMichael Roth         .description = "uint64_sanity1",
5342d496105SMichael Roth         .type = PTYPE_U64,
5352d496105SMichael Roth         .value.u64 = 1,
5362d496105SMichael Roth     },
5372d496105SMichael Roth     {
5382d496105SMichael Roth         .description = "uint64_sanity2",
5392d496105SMichael Roth         .type = PTYPE_U64,
5402d496105SMichael Roth         .value.u64 = UINT64_MAX / 2 + 1,
5412d496105SMichael Roth     },
5422d496105SMichael Roth     {
5432d496105SMichael Roth         .description = "uint64_min",
5442d496105SMichael Roth         .type = PTYPE_U64,
5452d496105SMichael Roth         .value.u64 = 0,
5462d496105SMichael Roth     },
5472d496105SMichael Roth     {
5482d496105SMichael Roth         .description = "uint64_max",
5492d496105SMichael Roth         .type = PTYPE_U64,
5502d496105SMichael Roth         .value.u64 = UINT64_MAX,
5512d496105SMichael Roth     },
5522d496105SMichael Roth     /* int8 tests */
5532d496105SMichael Roth     {
5542d496105SMichael Roth         .description = "int8_sanity1",
5552d496105SMichael Roth         .type = PTYPE_S8,
5562d496105SMichael Roth         .value.s8 = -1,
5572d496105SMichael Roth     },
5582d496105SMichael Roth     {
5592d496105SMichael Roth         .description = "int8_sanity2",
5602d496105SMichael Roth         .type = PTYPE_S8,
5612d496105SMichael Roth         .value.s8 = INT8_MAX / 2 + 1,
5622d496105SMichael Roth     },
5632d496105SMichael Roth     {
5642d496105SMichael Roth         .description = "int8_min",
5652d496105SMichael Roth         .type = PTYPE_S8,
5662d496105SMichael Roth         .value.s8 = INT8_MIN,
5672d496105SMichael Roth     },
5682d496105SMichael Roth     {
5692d496105SMichael Roth         .description = "int8_max",
5702d496105SMichael Roth         .type = PTYPE_S8,
5712d496105SMichael Roth         .value.s8 = INT8_MAX,
5722d496105SMichael Roth     },
5732d496105SMichael Roth     /* int16 tests */
5742d496105SMichael Roth     {
5752d496105SMichael Roth         .description = "int16_sanity1",
5762d496105SMichael Roth         .type = PTYPE_S16,
5772d496105SMichael Roth         .value.s16 = -1,
5782d496105SMichael Roth     },
5792d496105SMichael Roth     {
5802d496105SMichael Roth         .description = "int16_sanity2",
5812d496105SMichael Roth         .type = PTYPE_S16,
5822d496105SMichael Roth         .value.s16 = INT16_MAX / 2 + 1,
5832d496105SMichael Roth     },
5842d496105SMichael Roth     {
5852d496105SMichael Roth         .description = "int16_min",
5862d496105SMichael Roth         .type = PTYPE_S16,
5872d496105SMichael Roth         .value.s16 = INT16_MIN,
5882d496105SMichael Roth     },
5892d496105SMichael Roth     {
5902d496105SMichael Roth         .description = "int16_max",
5912d496105SMichael Roth         .type = PTYPE_S16,
5922d496105SMichael Roth         .value.s16 = INT16_MAX,
5932d496105SMichael Roth     },
5942d496105SMichael Roth     /* int32 tests */
5952d496105SMichael Roth     {
5962d496105SMichael Roth         .description = "int32_sanity1",
5972d496105SMichael Roth         .type = PTYPE_S32,
5982d496105SMichael Roth         .value.s32 = -1,
5992d496105SMichael Roth     },
6002d496105SMichael Roth     {
6012d496105SMichael Roth         .description = "int32_sanity2",
6022d496105SMichael Roth         .type = PTYPE_S32,
6032d496105SMichael Roth         .value.s32 = INT32_MAX / 2 + 1,
6042d496105SMichael Roth     },
6052d496105SMichael Roth     {
6062d496105SMichael Roth         .description = "int32_min",
6072d496105SMichael Roth         .type = PTYPE_S32,
6082d496105SMichael Roth         .value.s32 = INT32_MIN,
6092d496105SMichael Roth     },
6102d496105SMichael Roth     {
6112d496105SMichael Roth         .description = "int32_max",
6122d496105SMichael Roth         .type = PTYPE_S32,
6132d496105SMichael Roth         .value.s32 = INT32_MAX,
6142d496105SMichael Roth     },
6152d496105SMichael Roth     /* int64 tests */
6162d496105SMichael Roth     {
6172d496105SMichael Roth         .description = "int64_sanity1",
6182d496105SMichael Roth         .type = PTYPE_S64,
6192d496105SMichael Roth         .value.s64 = -1,
6202d496105SMichael Roth     },
6212d496105SMichael Roth     {
6222d496105SMichael Roth         .description = "int64_sanity2",
6232d496105SMichael Roth         .type = PTYPE_S64,
6242d496105SMichael Roth         .value.s64 = INT64_MAX / 2 + 1,
6252d496105SMichael Roth     },
6262d496105SMichael Roth     {
6272d496105SMichael Roth         .description = "int64_min",
6282d496105SMichael Roth         .type = PTYPE_S64,
6292d496105SMichael Roth         .value.s64 = INT64_MIN,
6302d496105SMichael Roth     },
6312d496105SMichael Roth     {
6322d496105SMichael Roth         .description = "int64_max",
6332d496105SMichael Roth         .type = PTYPE_S64,
6342d496105SMichael Roth         .value.s64 = INT64_MAX,
6352d496105SMichael Roth     },
6362d496105SMichael Roth     { .type = PTYPE_EOL }
6372d496105SMichael Roth };
6382d496105SMichael Roth 
6392d496105SMichael Roth /* visitor-specific op implementations */
6402d496105SMichael Roth 
6412d496105SMichael Roth typedef struct QmpSerializeData {
6422d496105SMichael Roth     QmpOutputVisitor *qov;
6432d496105SMichael Roth     QmpInputVisitor *qiv;
6442d496105SMichael Roth } QmpSerializeData;
6452d496105SMichael Roth 
6462d496105SMichael Roth static void qmp_serialize(void *native_in, void **datap,
6472d496105SMichael Roth                           VisitorFunc visit, Error **errp)
6482d496105SMichael Roth {
6492d496105SMichael Roth     QmpSerializeData *d = g_malloc0(sizeof(*d));
6502d496105SMichael Roth 
6512d496105SMichael Roth     d->qov = qmp_output_visitor_new();
6522d496105SMichael Roth     visit(qmp_output_get_visitor(d->qov), &native_in, errp);
6532d496105SMichael Roth     *datap = d;
6542d496105SMichael Roth }
6552d496105SMichael Roth 
6562d496105SMichael Roth static void qmp_deserialize(void **native_out, void *datap,
6572d496105SMichael Roth                             VisitorFunc visit, Error **errp)
6582d496105SMichael Roth {
6592d496105SMichael Roth     QmpSerializeData *d = datap;
660*ad7f375dSMichael Roth     QString *output_json;
661*ad7f375dSMichael Roth     QObject *obj_orig, *obj;
662*ad7f375dSMichael Roth 
663*ad7f375dSMichael Roth     obj_orig = qmp_output_get_qobject(d->qov);
664*ad7f375dSMichael Roth     output_json = qobject_to_json(obj_orig);
665*ad7f375dSMichael Roth     obj = qobject_from_json(qstring_get_str(output_json));
6662d496105SMichael Roth 
6672d496105SMichael Roth     QDECREF(output_json);
6682d496105SMichael Roth     d->qiv = qmp_input_visitor_new(obj);
669*ad7f375dSMichael Roth     qobject_decref(obj_orig);
6702bd01ac1SStefan Berger     qobject_decref(obj);
6712d496105SMichael Roth     visit(qmp_input_get_visitor(d->qiv), native_out, errp);
6722d496105SMichael Roth }
6732d496105SMichael Roth 
6742d496105SMichael Roth static void qmp_cleanup(void *datap)
6752d496105SMichael Roth {
6762d496105SMichael Roth     QmpSerializeData *d = datap;
6772d496105SMichael Roth     qmp_output_visitor_cleanup(d->qov);
6782d496105SMichael Roth     qmp_input_visitor_cleanup(d->qiv);
6792bd01ac1SStefan Berger 
6802bd01ac1SStefan Berger     g_free(d);
6812d496105SMichael Roth }
6822d496105SMichael Roth 
6830d30b0a2SMichael Roth typedef struct StringSerializeData {
6842bd01ac1SStefan Berger     char *string;
6850d30b0a2SMichael Roth     StringOutputVisitor *sov;
6860d30b0a2SMichael Roth     StringInputVisitor *siv;
6870d30b0a2SMichael Roth } StringSerializeData;
6880d30b0a2SMichael Roth 
6890d30b0a2SMichael Roth static void string_serialize(void *native_in, void **datap,
6900d30b0a2SMichael Roth                              VisitorFunc visit, Error **errp)
6910d30b0a2SMichael Roth {
6920d30b0a2SMichael Roth     StringSerializeData *d = g_malloc0(sizeof(*d));
6930d30b0a2SMichael Roth 
6940d30b0a2SMichael Roth     d->sov = string_output_visitor_new();
6950d30b0a2SMichael Roth     visit(string_output_get_visitor(d->sov), &native_in, errp);
6960d30b0a2SMichael Roth     *datap = d;
6970d30b0a2SMichael Roth }
6980d30b0a2SMichael Roth 
6990d30b0a2SMichael Roth static void string_deserialize(void **native_out, void *datap,
7000d30b0a2SMichael Roth                                VisitorFunc visit, Error **errp)
7010d30b0a2SMichael Roth {
7020d30b0a2SMichael Roth     StringSerializeData *d = datap;
7030d30b0a2SMichael Roth 
7042bd01ac1SStefan Berger     d->string = string_output_get_string(d->sov);
7052bd01ac1SStefan Berger     d->siv = string_input_visitor_new(d->string);
7060d30b0a2SMichael Roth     visit(string_input_get_visitor(d->siv), native_out, errp);
7070d30b0a2SMichael Roth }
7080d30b0a2SMichael Roth 
7090d30b0a2SMichael Roth static void string_cleanup(void *datap)
7100d30b0a2SMichael Roth {
7110d30b0a2SMichael Roth     StringSerializeData *d = datap;
7122bd01ac1SStefan Berger 
7130d30b0a2SMichael Roth     string_output_visitor_cleanup(d->sov);
7140d30b0a2SMichael Roth     string_input_visitor_cleanup(d->siv);
7152bd01ac1SStefan Berger     g_free(d->string);
7162bd01ac1SStefan Berger     g_free(d);
7170d30b0a2SMichael Roth }
7180d30b0a2SMichael Roth 
7192d496105SMichael Roth /* visitor registration, test harness */
7202d496105SMichael Roth 
7212d496105SMichael Roth /* note: to function interchangeably as a serialization mechanism your
7222d496105SMichael Roth  * visitor test implementation should pass the test cases for all visitor
7232d496105SMichael Roth  * capabilities: primitives, structures, and lists
7242d496105SMichael Roth  */
7252d496105SMichael Roth static const SerializeOps visitors[] = {
7262d496105SMichael Roth     {
7272d496105SMichael Roth         .type = "QMP",
7282d496105SMichael Roth         .serialize = qmp_serialize,
7292d496105SMichael Roth         .deserialize = qmp_deserialize,
7302d496105SMichael Roth         .cleanup = qmp_cleanup,
7312d496105SMichael Roth         .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS
7322d496105SMichael Roth     },
7330d30b0a2SMichael Roth     {
7340d30b0a2SMichael Roth         .type = "String",
7350d30b0a2SMichael Roth         .serialize = string_serialize,
7360d30b0a2SMichael Roth         .deserialize = string_deserialize,
7370d30b0a2SMichael Roth         .cleanup = string_cleanup,
7380d30b0a2SMichael Roth         .caps = VCAP_PRIMITIVES
7390d30b0a2SMichael Roth     },
7402d496105SMichael Roth     { NULL }
7412d496105SMichael Roth };
7422d496105SMichael Roth 
7432d496105SMichael Roth static void add_visitor_type(const SerializeOps *ops)
7442d496105SMichael Roth {
7452d496105SMichael Roth     char testname_prefix[128];
7462d496105SMichael Roth     char testname[128];
7472d496105SMichael Roth     TestArgs *args;
7482d496105SMichael Roth     int i = 0;
7492d496105SMichael Roth 
7502d496105SMichael Roth     sprintf(testname_prefix, "/visitor/serialization/%s", ops->type);
7512d496105SMichael Roth 
7522d496105SMichael Roth     if (ops->caps & VCAP_PRIMITIVES) {
7532d496105SMichael Roth         while (pt_values[i].type != PTYPE_EOL) {
7542d496105SMichael Roth             sprintf(testname, "%s/primitives/%s", testname_prefix,
7552d496105SMichael Roth                     pt_values[i].description);
7562d496105SMichael Roth             args = g_malloc0(sizeof(*args));
7572d496105SMichael Roth             args->ops = ops;
7582d496105SMichael Roth             args->test_data = &pt_values[i];
7592d496105SMichael Roth             g_test_add_data_func(testname, args, test_primitives);
7602d496105SMichael Roth             i++;
7612d496105SMichael Roth         }
7622d496105SMichael Roth     }
7632d496105SMichael Roth 
7642d496105SMichael Roth     if (ops->caps & VCAP_STRUCTURES) {
7652d496105SMichael Roth         sprintf(testname, "%s/struct", testname_prefix);
7662d496105SMichael Roth         args = g_malloc0(sizeof(*args));
7672d496105SMichael Roth         args->ops = ops;
7682d496105SMichael Roth         args->test_data = NULL;
7692d496105SMichael Roth         g_test_add_data_func(testname, args, test_struct);
7702d496105SMichael Roth 
7712d496105SMichael Roth         sprintf(testname, "%s/nested_struct", testname_prefix);
7722d496105SMichael Roth         args = g_malloc0(sizeof(*args));
7732d496105SMichael Roth         args->ops = ops;
7742d496105SMichael Roth         args->test_data = NULL;
7752d496105SMichael Roth         g_test_add_data_func(testname, args, test_nested_struct);
7762d496105SMichael Roth     }
7772d496105SMichael Roth 
7782d496105SMichael Roth     if (ops->caps & VCAP_LISTS) {
7792d496105SMichael Roth         sprintf(testname, "%s/nested_struct_list", testname_prefix);
7802d496105SMichael Roth         args = g_malloc0(sizeof(*args));
7812d496105SMichael Roth         args->ops = ops;
7822d496105SMichael Roth         args->test_data = NULL;
7832d496105SMichael Roth         g_test_add_data_func(testname, args, test_nested_struct_list);
7842d496105SMichael Roth     }
7852d496105SMichael Roth }
7862d496105SMichael Roth 
7872d496105SMichael Roth int main(int argc, char **argv)
7882d496105SMichael Roth {
7892d496105SMichael Roth     int i = 0;
7902d496105SMichael Roth 
7912d496105SMichael Roth     g_test_init(&argc, &argv, NULL);
7922d496105SMichael Roth 
7932d496105SMichael Roth     while (visitors[i].type != NULL) {
7942d496105SMichael Roth         add_visitor_type(&visitors[i]);
7952d496105SMichael Roth         i++;
7962d496105SMichael Roth     }
7972d496105SMichael Roth 
7982d496105SMichael Roth     g_test_run();
7992d496105SMichael Roth 
8002d496105SMichael Roth     return 0;
8012d496105SMichael Roth }
802