qemu/tests/test-visitor-serialization.c
<<
>>
Prefs
   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#include "test-qapi-types.h"
  18#include "test-qapi-visit.h"
  19#include "qemu-objects.h"
  20#include "qapi/qmp-input-visitor.h"
  21#include "qapi/qmp-output-visitor.h"
  22#include "qapi/string-input-visitor.h"
  23#include "qapi/string-output-visitor.h"
  24
  25typedef struct PrimitiveType {
  26    union {
  27        const char *string;
  28        bool boolean;
  29        double number;
  30        int64_t integer;
  31        uint8_t u8;
  32        uint16_t u16;
  33        uint32_t u32;
  34        uint64_t u64;
  35        int8_t s8;
  36        int16_t s16;
  37        int32_t s32;
  38        int64_t s64;
  39        intmax_t max;
  40    } value;
  41    enum {
  42        PTYPE_STRING = 0,
  43        PTYPE_BOOLEAN,
  44        PTYPE_NUMBER,
  45        PTYPE_INTEGER,
  46        PTYPE_U8,
  47        PTYPE_U16,
  48        PTYPE_U32,
  49        PTYPE_U64,
  50        PTYPE_S8,
  51        PTYPE_S16,
  52        PTYPE_S32,
  53        PTYPE_S64,
  54        PTYPE_EOL,
  55    } type;
  56    const char *description;
  57} PrimitiveType;
  58
  59/* test helpers */
  60
  61static void visit_primitive_type(Visitor *v, void **native, Error **errp)
  62{
  63    PrimitiveType *pt = *native;
  64    switch(pt->type) {
  65    case PTYPE_STRING:
  66        visit_type_str(v, (char **)&pt->value.string, NULL, errp);
  67        break;
  68    case PTYPE_BOOLEAN:
  69        visit_type_bool(v, &pt->value.boolean, NULL, errp);
  70        break;
  71    case PTYPE_NUMBER:
  72        visit_type_number(v, &pt->value.number, NULL, errp);
  73        break;
  74    case PTYPE_INTEGER:
  75        visit_type_int(v, &pt->value.integer, NULL, errp);
  76        break;
  77    case PTYPE_U8:
  78        visit_type_uint8(v, &pt->value.u8, NULL, errp);
  79        break;
  80    case PTYPE_U16:
  81        visit_type_uint16(v, &pt->value.u16, NULL, errp);
  82        break;
  83    case PTYPE_U32:
  84        visit_type_uint32(v, &pt->value.u32, NULL, errp);
  85        break;
  86    case PTYPE_U64:
  87        visit_type_uint64(v, &pt->value.u64, NULL, errp);
  88        break;
  89    case PTYPE_S8:
  90        visit_type_int8(v, &pt->value.s8, NULL, errp);
  91        break;
  92    case PTYPE_S16:
  93        visit_type_int16(v, &pt->value.s16, NULL, errp);
  94        break;
  95    case PTYPE_S32:
  96        visit_type_int32(v, &pt->value.s32, NULL, errp);
  97        break;
  98    case PTYPE_S64:
  99        visit_type_int64(v, &pt->value.s64, NULL, errp);
 100        break;
 101    case PTYPE_EOL:
 102        g_assert(false);
 103    }
 104}
 105
 106typedef struct TestStruct
 107{
 108    int64_t integer;
 109    bool boolean;
 110    char *string;
 111} TestStruct;
 112
 113static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
 114                                  const char *name, Error **errp)
 115{
 116    visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), errp);
 117
 118    visit_type_int(v, &(*obj)->integer, "integer", errp);
 119    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
 120    visit_type_str(v, &(*obj)->string, "string", errp);
 121
 122    visit_end_struct(v, errp);
 123}
 124
 125static TestStruct *struct_create(void)
 126{
 127    TestStruct *ts = g_malloc0(sizeof(*ts));
 128    ts->integer = -42;
 129    ts->boolean = true;
 130    ts->string = strdup("test string");
 131    return ts;
 132}
 133
 134static void struct_compare(TestStruct *ts1, TestStruct *ts2)
 135{
 136    g_assert(ts1);
 137    g_assert(ts2);
 138    g_assert_cmpint(ts1->integer, ==, ts2->integer);
 139    g_assert(ts1->boolean == ts2->boolean);
 140    g_assert_cmpstr(ts1->string, ==, ts2->string);
 141}
 142
 143static void struct_cleanup(TestStruct *ts)
 144{
 145    g_free(ts->string);
 146    g_free(ts);
 147}
 148
 149static void visit_struct(Visitor *v, void **native, Error **errp)
 150{
 151    visit_type_TestStruct(v, (TestStruct **)native, NULL, errp);
 152}
 153
 154static UserDefNested *nested_struct_create(void)
 155{
 156    UserDefNested *udnp = g_malloc0(sizeof(*udnp));
 157    udnp->string0 = strdup("test_string0");
 158    udnp->dict1.string1 = strdup("test_string1");
 159    udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
 160    udnp->dict1.dict2.userdef1->integer = 42;
 161    udnp->dict1.dict2.userdef1->string = strdup("test_string");
 162    udnp->dict1.dict2.string2 = strdup("test_string2");
 163    udnp->dict1.has_dict3 = true;
 164    udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
 165    udnp->dict1.dict3.userdef2->integer = 43;
 166    udnp->dict1.dict3.userdef2->string = strdup("test_string");
 167    udnp->dict1.dict3.string3 = strdup("test_string3");
 168    return udnp;
 169}
 170
 171static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2)
 172{
 173    g_assert(udnp1);
 174    g_assert(udnp2);
 175    g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
 176    g_assert_cmpstr(udnp1->dict1.string1, ==, udnp2->dict1.string1);
 177    g_assert_cmpint(udnp1->dict1.dict2.userdef1->integer, ==,
 178                    udnp2->dict1.dict2.userdef1->integer);
 179    g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==,
 180                    udnp2->dict1.dict2.userdef1->string);
 181    g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2);
 182    g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3);
 183    g_assert_cmpint(udnp1->dict1.dict3.userdef2->integer, ==,
 184                    udnp2->dict1.dict3.userdef2->integer);
 185    g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==,
 186                    udnp2->dict1.dict3.userdef2->string);
 187    g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3);
 188}
 189
 190static void nested_struct_cleanup(UserDefNested *udnp)
 191{
 192    qapi_free_UserDefNested(udnp);
 193}
 194
 195static void visit_nested_struct(Visitor *v, void **native, Error **errp)
 196{
 197    visit_type_UserDefNested(v, (UserDefNested **)native, NULL, errp);
 198}
 199
 200static void visit_nested_struct_list(Visitor *v, void **native, Error **errp)
 201{
 202    visit_type_UserDefNestedList(v, (UserDefNestedList **)native, NULL, errp);
 203}
 204
 205/* test cases */
 206
 207typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp);
 208
 209typedef enum VisitorCapabilities {
 210    VCAP_PRIMITIVES = 1,
 211    VCAP_STRUCTURES = 2,
 212    VCAP_LISTS = 4,
 213} VisitorCapabilities;
 214
 215typedef struct SerializeOps {
 216    void (*serialize)(void *native_in, void **datap,
 217                      VisitorFunc visit, Error **errp);
 218    void (*deserialize)(void **native_out, void *datap,
 219                            VisitorFunc visit, Error **errp);
 220    void (*cleanup)(void *datap);
 221    const char *type;
 222    VisitorCapabilities caps;
 223} SerializeOps;
 224
 225typedef struct TestArgs {
 226    const SerializeOps *ops;
 227    void *test_data;
 228} TestArgs;
 229
 230#define FLOAT_STRING_PRECISION 6 /* corresponding to n in %.nf formatting */
 231static gsize calc_float_string_storage(double value)
 232{
 233    int whole_value = value;
 234    gsize i = 0;
 235    do {
 236        i++;
 237    } while (whole_value /= 10);
 238    return i + 2 + FLOAT_STRING_PRECISION;
 239}
 240
 241static void test_primitives(gconstpointer opaque)
 242{
 243    TestArgs *args = (TestArgs *) opaque;
 244    const SerializeOps *ops = args->ops;
 245    PrimitiveType *pt = args->test_data;
 246    PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy));
 247    Error *err = NULL;
 248    void *serialize_data;
 249    char *double1, *double2;
 250
 251    pt_copy->type = pt->type;
 252    ops->serialize(pt, &serialize_data, visit_primitive_type, &err);
 253    ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, &err);
 254
 255    g_assert(err == NULL);
 256    g_assert(pt_copy != NULL);
 257    if (pt->type == PTYPE_STRING) {
 258        g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string);
 259    } else if (pt->type == PTYPE_NUMBER) {
 260        /* we serialize with %f for our reference visitors, so rather than fuzzy
 261         * floating math to test "equality", just compare the formatted values
 262         */
 263        double1 = g_malloc0(calc_float_string_storage(pt->value.number));
 264        double2 = g_malloc0(calc_float_string_storage(pt_copy->value.number));
 265        g_assert_cmpstr(double1, ==, double2);
 266        g_free(double1);
 267        g_free(double2);
 268    } else if (pt->type == PTYPE_BOOLEAN) {
 269        g_assert_cmpint(!!pt->value.max, ==, !!pt->value.max);
 270    } else {
 271        g_assert_cmpint(pt->value.max, ==, pt_copy->value.max);
 272    }
 273
 274    ops->cleanup(serialize_data);
 275    g_free(args);
 276}
 277
 278static void test_struct(gconstpointer opaque)
 279{
 280    TestArgs *args = (TestArgs *) opaque;
 281    const SerializeOps *ops = args->ops;
 282    TestStruct *ts = struct_create();
 283    TestStruct *ts_copy = NULL;
 284    Error *err = NULL;
 285    void *serialize_data;
 286
 287    ops->serialize(ts, &serialize_data, visit_struct, &err);
 288    ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, &err); 
 289
 290    g_assert(err == NULL);
 291    struct_compare(ts, ts_copy);
 292
 293    struct_cleanup(ts);
 294    struct_cleanup(ts_copy);
 295
 296    ops->cleanup(serialize_data);
 297    g_free(args);
 298}
 299
 300static void test_nested_struct(gconstpointer opaque)
 301{
 302    TestArgs *args = (TestArgs *) opaque;
 303    const SerializeOps *ops = args->ops;
 304    UserDefNested *udnp = nested_struct_create();
 305    UserDefNested *udnp_copy = NULL;
 306    Error *err = NULL;
 307    void *serialize_data;
 308    
 309    ops->serialize(udnp, &serialize_data, visit_nested_struct, &err);
 310    ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, &err); 
 311
 312    g_assert(err == NULL);
 313    nested_struct_compare(udnp, udnp_copy);
 314
 315    nested_struct_cleanup(udnp);
 316    nested_struct_cleanup(udnp_copy);
 317
 318    ops->cleanup(serialize_data);
 319    g_free(args);
 320}
 321
 322static void test_nested_struct_list(gconstpointer opaque)
 323{
 324    TestArgs *args = (TestArgs *) opaque;
 325    const SerializeOps *ops = args->ops;
 326    UserDefNestedList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
 327    Error *err = NULL;
 328    void *serialize_data;
 329    int i = 0;
 330
 331    for (i = 0; i < 8; i++) {
 332        tmp = g_malloc0(sizeof(UserDefNestedList));
 333        tmp->value = nested_struct_create();
 334        tmp->next = listp;
 335        listp = tmp;
 336    }
 337    
 338    ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
 339    ops->deserialize((void **)&listp_copy, serialize_data,
 340                     visit_nested_struct_list, &err); 
 341
 342    g_assert(err == NULL);
 343
 344    tmp = listp;
 345    tmp_copy = listp_copy;
 346    while (listp_copy) {
 347        g_assert(listp);
 348        nested_struct_compare(listp->value, listp_copy->value);
 349        listp = listp->next;
 350        listp_copy = listp_copy->next;
 351    }
 352
 353    qapi_free_UserDefNestedList(tmp);
 354    qapi_free_UserDefNestedList(tmp_copy);
 355
 356    ops->cleanup(serialize_data);
 357    g_free(args);
 358}
 359
 360PrimitiveType pt_values[] = {
 361    /* string tests */
 362    {
 363        .description = "string_empty",
 364        .type = PTYPE_STRING,
 365        .value.string = "",
 366    },
 367    {
 368        .description = "string_whitespace",
 369        .type = PTYPE_STRING,
 370        .value.string = "a b  c\td",
 371    },
 372    {
 373        .description = "string_newlines",
 374        .type = PTYPE_STRING,
 375        .value.string = "a\nb\n",
 376    },
 377    {
 378        .description = "string_commas",
 379        .type = PTYPE_STRING,
 380        .value.string = "a,b, c,d",
 381    },
 382    {
 383        .description = "string_single_quoted",
 384        .type = PTYPE_STRING,
 385        .value.string = "'a b',cd",
 386    },
 387    {
 388        .description = "string_double_quoted",
 389        .type = PTYPE_STRING,
 390        .value.string = "\"a b\",cd",
 391    },
 392    /* boolean tests */
 393    {
 394        .description = "boolean_true1",
 395        .type = PTYPE_BOOLEAN,
 396        .value.boolean = true,
 397    },
 398    {
 399        .description = "boolean_true2",
 400        .type = PTYPE_BOOLEAN,
 401        .value.boolean = 8,
 402    },
 403    {
 404        .description = "boolean_true3",
 405        .type = PTYPE_BOOLEAN,
 406        .value.boolean = -1,
 407    },
 408    {
 409        .description = "boolean_false1",
 410        .type = PTYPE_BOOLEAN,
 411        .value.boolean = false,
 412    },
 413    {
 414        .description = "boolean_false2",
 415        .type = PTYPE_BOOLEAN,
 416        .value.boolean = 0,
 417    },
 418    /* number tests (double) */
 419    /* note: we format these to %.6f before comparing, since that's how
 420     * we serialize them and it doesn't make sense to check precision
 421     * beyond that.
 422     */
 423    {
 424        .description = "number_sanity1",
 425        .type = PTYPE_NUMBER,
 426        .value.number = -1,
 427    },
 428    {
 429        .description = "number_sanity2",
 430        .type = PTYPE_NUMBER,
 431        .value.number = 3.14159265,
 432    },
 433    {
 434        .description = "number_min",
 435        .type = PTYPE_NUMBER,
 436        .value.number = DBL_MIN,
 437    },
 438    {
 439        .description = "number_max",
 440        .type = PTYPE_NUMBER,
 441        .value.number = DBL_MAX,
 442    },
 443    /* integer tests (int64) */
 444    {
 445        .description = "integer_sanity1",
 446        .type = PTYPE_INTEGER,
 447        .value.integer = -1,
 448    },
 449    {
 450        .description = "integer_sanity2",
 451        .type = PTYPE_INTEGER,
 452        .value.integer = INT64_MAX / 2 + 1,
 453    },
 454    {
 455        .description = "integer_min",
 456        .type = PTYPE_INTEGER,
 457        .value.integer = INT64_MIN,
 458    },
 459    {
 460        .description = "integer_max",
 461        .type = PTYPE_INTEGER,
 462        .value.integer = INT64_MAX,
 463    },
 464    /* uint8 tests */
 465    {
 466        .description = "uint8_sanity1",
 467        .type = PTYPE_U8,
 468        .value.u8 = 1,
 469    },
 470    {
 471        .description = "uint8_sanity2",
 472        .type = PTYPE_U8,
 473        .value.u8 = UINT8_MAX / 2 + 1,
 474    },
 475    {
 476        .description = "uint8_min",
 477        .type = PTYPE_U8,
 478        .value.u8 = 0,
 479    },
 480    {
 481        .description = "uint8_max",
 482        .type = PTYPE_U8,
 483        .value.u8 = UINT8_MAX,
 484    },
 485    /* uint16 tests */
 486    {
 487        .description = "uint16_sanity1",
 488        .type = PTYPE_U16,
 489        .value.u16 = 1,
 490    },
 491    {
 492        .description = "uint16_sanity2",
 493        .type = PTYPE_U16,
 494        .value.u16 = UINT16_MAX / 2 + 1,
 495    },
 496    {
 497        .description = "uint16_min",
 498        .type = PTYPE_U16,
 499        .value.u16 = 0,
 500    },
 501    {
 502        .description = "uint16_max",
 503        .type = PTYPE_U16,
 504        .value.u16 = UINT16_MAX,
 505    },
 506    /* uint32 tests */
 507    {
 508        .description = "uint32_sanity1",
 509        .type = PTYPE_U32,
 510        .value.u32 = 1,
 511    },
 512    {
 513        .description = "uint32_sanity2",
 514        .type = PTYPE_U32,
 515        .value.u32 = UINT32_MAX / 2 + 1,
 516    },
 517    {
 518        .description = "uint32_min",
 519        .type = PTYPE_U32,
 520        .value.u32 = 0,
 521    },
 522    {
 523        .description = "uint32_max",
 524        .type = PTYPE_U32,
 525        .value.u32 = UINT32_MAX,
 526    },
 527    /* uint64 tests */
 528    {
 529        .description = "uint64_sanity1",
 530        .type = PTYPE_U64,
 531        .value.u64 = 1,
 532    },
 533    {
 534        .description = "uint64_sanity2",
 535        .type = PTYPE_U64,
 536        .value.u64 = UINT64_MAX / 2 + 1,
 537    },
 538    {
 539        .description = "uint64_min",
 540        .type = PTYPE_U64,
 541        .value.u64 = 0,
 542    },
 543    {
 544        .description = "uint64_max",
 545        .type = PTYPE_U64,
 546        .value.u64 = UINT64_MAX,
 547    },
 548    /* int8 tests */
 549    {
 550        .description = "int8_sanity1",
 551        .type = PTYPE_S8,
 552        .value.s8 = -1,
 553    },
 554    {
 555        .description = "int8_sanity2",
 556        .type = PTYPE_S8,
 557        .value.s8 = INT8_MAX / 2 + 1,
 558    },
 559    {
 560        .description = "int8_min",
 561        .type = PTYPE_S8,
 562        .value.s8 = INT8_MIN,
 563    },
 564    {
 565        .description = "int8_max",
 566        .type = PTYPE_S8,
 567        .value.s8 = INT8_MAX,
 568    },
 569    /* int16 tests */
 570    {
 571        .description = "int16_sanity1",
 572        .type = PTYPE_S16,
 573        .value.s16 = -1,
 574    },
 575    {
 576        .description = "int16_sanity2",
 577        .type = PTYPE_S16,
 578        .value.s16 = INT16_MAX / 2 + 1,
 579    },
 580    {
 581        .description = "int16_min",
 582        .type = PTYPE_S16,
 583        .value.s16 = INT16_MIN,
 584    },
 585    {
 586        .description = "int16_max",
 587        .type = PTYPE_S16,
 588        .value.s16 = INT16_MAX,
 589    },
 590    /* int32 tests */
 591    {
 592        .description = "int32_sanity1",
 593        .type = PTYPE_S32,
 594        .value.s32 = -1,
 595    },
 596    {
 597        .description = "int32_sanity2",
 598        .type = PTYPE_S32,
 599        .value.s32 = INT32_MAX / 2 + 1,
 600    },
 601    {
 602        .description = "int32_min",
 603        .type = PTYPE_S32,
 604        .value.s32 = INT32_MIN,
 605    },
 606    {
 607        .description = "int32_max",
 608        .type = PTYPE_S32,
 609        .value.s32 = INT32_MAX,
 610    },
 611    /* int64 tests */
 612    {
 613        .description = "int64_sanity1",
 614        .type = PTYPE_S64,
 615        .value.s64 = -1,
 616    },
 617    {
 618        .description = "int64_sanity2",
 619        .type = PTYPE_S64,
 620        .value.s64 = INT64_MAX / 2 + 1,
 621    },
 622    {
 623        .description = "int64_min",
 624        .type = PTYPE_S64,
 625        .value.s64 = INT64_MIN,
 626    },
 627    {
 628        .description = "int64_max",
 629        .type = PTYPE_S64,
 630        .value.s64 = INT64_MAX,
 631    },
 632    { .type = PTYPE_EOL }
 633};
 634
 635/* visitor-specific op implementations */
 636
 637typedef struct QmpSerializeData {
 638    QmpOutputVisitor *qov;
 639    QmpInputVisitor *qiv;
 640} QmpSerializeData;
 641
 642static void qmp_serialize(void *native_in, void **datap,
 643                          VisitorFunc visit, Error **errp)
 644{
 645    QmpSerializeData *d = g_malloc0(sizeof(*d));
 646
 647    d->qov = qmp_output_visitor_new();
 648    visit(qmp_output_get_visitor(d->qov), &native_in, errp);
 649    *datap = d;
 650}
 651
 652static void qmp_deserialize(void **native_out, void *datap,
 653                            VisitorFunc visit, Error **errp)
 654{
 655    QmpSerializeData *d = datap;
 656    QString *output_json = qobject_to_json(qmp_output_get_qobject(d->qov));
 657    QObject *obj = qobject_from_json(qstring_get_str(output_json));
 658
 659    QDECREF(output_json);
 660    d->qiv = qmp_input_visitor_new(obj);
 661    visit(qmp_input_get_visitor(d->qiv), native_out, errp);
 662}
 663
 664static void qmp_cleanup(void *datap)
 665{
 666    QmpSerializeData *d = datap;
 667    qmp_output_visitor_cleanup(d->qov);
 668    qmp_input_visitor_cleanup(d->qiv);
 669}
 670
 671typedef struct StringSerializeData {
 672    StringOutputVisitor *sov;
 673    StringInputVisitor *siv;
 674} StringSerializeData;
 675
 676static void string_serialize(void *native_in, void **datap,
 677                             VisitorFunc visit, Error **errp)
 678{
 679    StringSerializeData *d = g_malloc0(sizeof(*d));
 680
 681    d->sov = string_output_visitor_new();
 682    visit(string_output_get_visitor(d->sov), &native_in, errp);
 683    *datap = d;
 684}
 685
 686static void string_deserialize(void **native_out, void *datap,
 687                               VisitorFunc visit, Error **errp)
 688{
 689    StringSerializeData *d = datap;
 690
 691    d->siv = string_input_visitor_new(string_output_get_string(d->sov));
 692    visit(string_input_get_visitor(d->siv), native_out, errp);
 693}
 694
 695static void string_cleanup(void *datap)
 696{
 697    StringSerializeData *d = datap;
 698    string_output_visitor_cleanup(d->sov);
 699    string_input_visitor_cleanup(d->siv);
 700}
 701
 702/* visitor registration, test harness */
 703
 704/* note: to function interchangeably as a serialization mechanism your
 705 * visitor test implementation should pass the test cases for all visitor
 706 * capabilities: primitives, structures, and lists
 707 */
 708static const SerializeOps visitors[] = {
 709    {
 710        .type = "QMP",
 711        .serialize = qmp_serialize,
 712        .deserialize = qmp_deserialize,
 713        .cleanup = qmp_cleanup,
 714        .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS
 715    },
 716    {
 717        .type = "String",
 718        .serialize = string_serialize,
 719        .deserialize = string_deserialize,
 720        .cleanup = string_cleanup,
 721        .caps = VCAP_PRIMITIVES
 722    },
 723    { NULL }
 724};
 725
 726static void add_visitor_type(const SerializeOps *ops)
 727{
 728    char testname_prefix[128];
 729    char testname[128];
 730    TestArgs *args;
 731    int i = 0;
 732
 733    sprintf(testname_prefix, "/visitor/serialization/%s", ops->type);
 734
 735    if (ops->caps & VCAP_PRIMITIVES) {
 736        while (pt_values[i].type != PTYPE_EOL) {
 737            sprintf(testname, "%s/primitives/%s", testname_prefix,
 738                    pt_values[i].description);
 739            args = g_malloc0(sizeof(*args));
 740            args->ops = ops;
 741            args->test_data = &pt_values[i];
 742            g_test_add_data_func(testname, args, test_primitives);
 743            i++;
 744        }
 745    }
 746
 747    if (ops->caps & VCAP_STRUCTURES) {
 748        sprintf(testname, "%s/struct", testname_prefix);
 749        args = g_malloc0(sizeof(*args));
 750        args->ops = ops;
 751        args->test_data = NULL;
 752        g_test_add_data_func(testname, args, test_struct);
 753
 754        sprintf(testname, "%s/nested_struct", testname_prefix);
 755        args = g_malloc0(sizeof(*args));
 756        args->ops = ops;
 757        args->test_data = NULL;
 758        g_test_add_data_func(testname, args, test_nested_struct);
 759    }
 760
 761    if (ops->caps & VCAP_LISTS) {
 762        sprintf(testname, "%s/nested_struct_list", testname_prefix);
 763        args = g_malloc0(sizeof(*args));
 764        args->ops = ops;
 765        args->test_data = NULL;
 766        g_test_add_data_func(testname, args, test_nested_struct_list);
 767    }
 768}
 769
 770int main(int argc, char **argv)
 771{
 772    int i = 0;
 773
 774    g_test_init(&argc, &argv, NULL);
 775
 776    while (visitors[i].type != NULL) {
 777        add_visitor_type(&visitors[i]);
 778        i++;
 779    }
 780
 781    g_test_run();
 782
 783    return 0;
 784}
 785