diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h new file mode 100644 index 0000000000..16ceff55dd --- /dev/null +++ b/include/qapi/clone-visitor.h @@ -0,0 +1,37 @@ +/* + * Clone Visitor + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QAPI_CLONE_VISITOR_H +#define QAPI_CLONE_VISITOR_H + +#include "qapi/visitor.h" + +/* + * The clone visitor is for direct use only by the QAPI_CLONE() macro; + * it requires that the root visit occur on an object, list, or + * alternate, and is not usable directly on built-in QAPI types. + */ +typedef struct QapiCloneVisitor QapiCloneVisitor; + +void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *, + void **, Error **)); + +/* + * Deep-clone QAPI object @src of the given @type, and return the result. + * + * Not usable on QAPI scalars (integers, strings, enums), nor on a + * QAPI object that references the 'any' type. Safe when @src is NULL. + */ +#define QAPI_CLONE(type, src) \ + ((type *)qapi_clone(src, \ + (void (*)(Visitor *, const char *, void**, \ + Error **))visit_type_ ## type)) + +#endif diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index 16e0b86d57..8bd47ee4bb 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -27,14 +27,18 @@ */ /* - * There are three classes of visitors; setting the class determines + * There are four classes of visitors; setting the class determines * how QAPI enums are visited, as well as what additional restrictions - * can be asserted. + * can be asserted. The values are intentionally chosen so as to + * permit some assertions based on whether a given bit is set (that + * is, some assertions apply to input and clone visitors, some + * assertions apply to output and clone visitors). */ typedef enum VisitorType { - VISITOR_INPUT, - VISITOR_OUTPUT, - VISITOR_DEALLOC, + VISITOR_INPUT = 1, + VISITOR_OUTPUT = 2, + VISITOR_CLONE = 3, + VISITOR_DEALLOC = 4, } VisitorType; struct Visitor diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 00a60eaaab..fb8f4eb6ec 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -24,15 +24,16 @@ * for doing work at each node of a QAPI graph; it can also be used * for a virtual walk, where there is no actual QAPI C struct. * - * There are three kinds of visitor classes: input visitors (QMP, + * There are four kinds of visitor classes: input visitors (QMP, * string, and QemuOpts) parse an external representation and build * the corresponding QAPI graph, output visitors (QMP and string) take - * a completed QAPI graph and generate an external representation, and - * the dealloc visitor can take a QAPI graph (possibly partially - * constructed) and recursively free its resources. While the dealloc - * and QMP input/output visitors are general, the string and QemuOpts - * visitors have some implementation limitations; see the - * documentation for each visitor for more details on what it + * a completed QAPI graph and generate an external representation, the + * dealloc visitor can take a QAPI graph (possibly partially + * constructed) and recursively free its resources, and the clone + * visitor performs a deep clone of one QAPI object to another. While + * the dealloc and QMP input/output visitors are general, the string, + * QemuOpts, and clone visitors have some implementation limitations; + * see the documentation for each visitor for more details on what it * supports. Also, see visitor-impl.h for the callback contracts * implemented by each visitor, and docs/qapi-code-gen.txt for more * about the QAPI code generator. @@ -80,9 +81,9 @@ * * If an error is detected during visit_type_FOO() with an input * visitor, then *@obj will be NULL for pointer types, and left - * unchanged for scalar types. Using an output visitor with an - * incomplete object has undefined behavior (other than a special case - * for visit_type_str() treating NULL like ""), while the dealloc + * unchanged for scalar types. Using an output or clone visitor with + * an incomplete object has undefined behavior (other than a special + * case for visit_type_str() treating NULL like ""), while the dealloc * visitor safely handles incomplete objects. Since input visitors * never produce an incomplete object, such an object is possible only * by manual construction. @@ -102,11 +103,19 @@ * * void qapi_free_FOO(FOO *obj); * - * which behaves like free() in that @obj may be NULL. Because of - * these functions, the dealloc visitor is seldom used directly - * outside of generated code. QAPI types can also inherit from a base - * class; when this happens, a function is generated for easily going - * from the derived type to the base type: + * where behaves like free() in that @obj may be NULL. Such objects + * may also be used with the following macro, provided alongside the + * clone visitor: + * + * Type *QAPI_CLONE(Type, src); + * + * in order to perform a deep clone of @src. Because of the generated + * qapi_free functions and the QAPI_CLONE() macro, the clone and + * dealloc visitor should not be used directly outside of QAPI code. + * + * QAPI types can also inherit from a base class; when this happens, a + * function is generated for easily going from the derived type to the + * base type: * * BASE *qapi_CHILD_base(CHILD *obj); * @@ -272,9 +281,9 @@ void visit_free(Visitor *v); * container; see the general description of @name above. * * @obj must be non-NULL for a real walk, in which case @size - * determines how much memory an input visitor will allocate into - * *@obj. @obj may also be NULL for a virtual walk, in which case - * @size is ignored. + * determines how much memory an input or clone visitor will allocate + * into *@obj. @obj may also be NULL for a virtual walk, in which + * case @size is ignored. * * @errp obeys typical error usage, and reports failures such as a * member @name is not present, or present but not an object. On @@ -327,9 +336,9 @@ void visit_end_struct(Visitor *v, void **obj); * container; see the general description of @name above. * * @list must be non-NULL for a real walk, in which case @size - * determines how much memory an input visitor will allocate into - * *@list (at least sizeof(GenericList)). Some visitors also allow - * @list to be NULL for a virtual walk, in which case @size is + * determines how much memory an input or clone visitor will allocate + * into *@list (at least sizeof(GenericList)). Some visitors also + * allow @list to be NULL for a virtual walk, in which case @size is * ignored. * * @errp obeys typical error usage, and reports failures such as a @@ -386,10 +395,10 @@ void visit_end_list(Visitor *v, void **list); * @name expresses the relationship of this alternate to its parent * container; see the general description of @name above. * - * @obj must not be NULL. Input visitors use @size to determine how - * much memory to allocate into *@obj, then determine the qtype of the - * next thing to be visited, stored in (*@obj)->type. Other visitors - * will leave @obj unchanged. + * @obj must not be NULL. Input and clone visitors use @size to + * determine how much memory to allocate into *@obj, then determine + * the qtype of the next thing to be visited, stored in (*@obj)->type. + * Other visitors will leave @obj unchanged. * * If @promote_int, treat integers as QTYPE_FLOAT. * @@ -554,9 +563,10 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp); * @name expresses the relationship of this string to its parent * container; see the general description of @name above. * - * @obj must be non-NULL. Input visitors set *@obj to the value - * (never NULL). Other visitors leave *@obj unchanged, and commonly - * treat NULL like "". + * @obj must be non-NULL. Input and clone visitors set *@obj to the + * value (always using "" rather than NULL for an empty string). + * Other visitors leave *@obj unchanged, and commonly treat NULL like + * "". * * It is safe to cast away const when preparing a (const char *) value * into @obj for use by an output visitor. diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 2278970690..7ea4aebb00 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -1,6 +1,6 @@ util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o util-obj-y += string-input-visitor.o string-output-visitor.o -util-obj-y += opts-visitor.o +util-obj-y += opts-visitor.o qapi-clone-visitor.o util-obj-y += qmp-event.o util-obj-y += qapi-util.o diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c new file mode 100644 index 0000000000..0bb8216372 --- /dev/null +++ b/qapi/qapi-clone-visitor.c @@ -0,0 +1,182 @@ +/* + * Copy one QAPI object to another + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/clone-visitor.h" +#include "qapi/visitor-impl.h" +#include "qapi/error.h" + +struct QapiCloneVisitor { + Visitor visitor; + size_t depth; +}; + +static QapiCloneVisitor *to_qcv(Visitor *v) +{ + return container_of(v, QapiCloneVisitor, visitor); +} + +static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + if (!obj) { + assert(qcv->depth); + /* Only possible when visiting an alternate's object + * branch. Nothing further to do here, since the earlier + * visit_start_alternate() already copied memory. */ + return; + } + + *obj = g_memdup(*obj, size); + qcv->depth++; +} + +static void qapi_clone_end(Visitor *v, void **obj) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + if (obj) { + qcv->depth--; + } +} + +static void qapi_clone_start_list(Visitor *v, const char *name, + GenericList **listp, size_t size, + Error **errp) +{ + qapi_clone_start_struct(v, name, (void **)listp, size, errp); +} + +static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail, + size_t size) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Unshare the tail of the list cloned by g_memdup() */ + tail->next = g_memdup(tail->next, size); + return tail->next; +} + +static void qapi_clone_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp) +{ + qapi_clone_start_struct(v, name, (void **)obj, size, errp); +} + +static void qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_uint64(Visitor *v, const char *name, + uint64_t *obj, Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_bool(Visitor *v, const char *name, bool *obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_str(Visitor *v, const char *name, char **obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* + * Pointer was already cloned by g_memdup; create fresh copy. + * Note that as long as qmp-output-visitor accepts NULL instead of + * "", then we must do likewise. However, we want to obey the + * input visitor semantics of never producing NULL when the empty + * string is intended. + */ + *obj = g_strdup(*obj ?: ""); +} + +static void qapi_clone_type_number(Visitor *v, const char *name, double *obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Nothing to do */ +} + +static void qapi_clone_free(Visitor *v) +{ + g_free(v); +} + +static Visitor *qapi_clone_visitor_new(void) +{ + QapiCloneVisitor *v; + + v = g_malloc0(sizeof(*v)); + + v->visitor.type = VISITOR_CLONE; + v->visitor.start_struct = qapi_clone_start_struct; + v->visitor.end_struct = qapi_clone_end; + v->visitor.start_list = qapi_clone_start_list; + v->visitor.next_list = qapi_clone_next_list; + v->visitor.end_list = qapi_clone_end; + v->visitor.start_alternate = qapi_clone_start_alternate; + v->visitor.end_alternate = qapi_clone_end; + v->visitor.type_int64 = qapi_clone_type_int64; + v->visitor.type_uint64 = qapi_clone_type_uint64; + v->visitor.type_bool = qapi_clone_type_bool; + v->visitor.type_str = qapi_clone_type_str; + v->visitor.type_number = qapi_clone_type_number; + v->visitor.type_null = qapi_clone_type_null; + v->visitor.free = qapi_clone_free; + + return &v->visitor; +} + +void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *, + void **, Error **)) +{ + Visitor *v; + void *dst = (void *) src; /* Cast away const */ + + if (!src) { + return NULL; + } + + v = qapi_clone_visitor_new(); + visit_type(v, NULL, &dst, &error_abort); + visit_free(v); + return dst; +} diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index eb7dd7253c..55f5876dc0 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -42,10 +42,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj, if (obj) { assert(size); - assert(v->type != VISITOR_OUTPUT || *obj); + assert(!(v->type & VISITOR_OUTPUT) || *obj); } v->start_struct(v, name, obj, size, &err); - if (obj && v->type == VISITOR_INPUT) { + if (obj && (v->type & VISITOR_INPUT)) { assert(!err != !*obj); } error_propagate(errp, err); @@ -70,7 +70,7 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list, assert(!list || size >= sizeof(GenericList)); v->start_list(v, name, list, size, &err); - if (list && v->type == VISITOR_INPUT) { + if (list && (v->type & VISITOR_INPUT)) { assert(!(err && *list)); } error_propagate(errp, err); @@ -94,11 +94,11 @@ void visit_start_alternate(Visitor *v, const char *name, Error *err = NULL; assert(obj && size >= sizeof(GenericAlternate)); - assert(v->type != VISITOR_OUTPUT || *obj); + assert(!(v->type & VISITOR_OUTPUT) || *obj); if (v->start_alternate) { v->start_alternate(v, name, obj, size, promote_int, &err); } - if (v->type == VISITOR_INPUT) { + if (v->type & VISITOR_INPUT) { assert(v->start_alternate && !err != !*obj); } error_propagate(errp, err); @@ -250,10 +250,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) assert(obj); /* TODO: Fix callers to not pass NULL when they mean "", so that we * can enable: - assert(v->type != VISITOR_OUTPUT || *obj); + assert(!(v->type & VISITOR_OUTPUT) || *obj); */ v->type_str(v, name, obj, &err); - if (v->type == VISITOR_INPUT) { + if (v->type & VISITOR_INPUT) { assert(!err != !*obj); } error_propagate(errp, err); @@ -335,9 +335,19 @@ void visit_type_enum(Visitor *v, const char *name, int *obj, const char *const strings[], Error **errp) { assert(obj && strings); - if (v->type == VISITOR_INPUT) { + switch (v->type) { + case VISITOR_INPUT: input_type_enum(v, name, obj, strings, errp); - } else if (v->type == VISITOR_OUTPUT) { + break; + case VISITOR_OUTPUT: output_type_enum(v, name, obj, strings, errp); + break; + case VISITOR_CLONE: + /* nothing further to do, scalar value was already copied by + * g_memdup() during visit_start_*() */ + break; + case VISITOR_DEALLOC: + /* nothing to deallocate for a scalar */ + break; } } diff --git a/tests/.gitignore b/tests/.gitignore index 840ea396ab..dbb52639f6 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -13,6 +13,7 @@ test-aio test-base64 test-bitops test-blockjob-txn +test-clone-visitor test-coroutine test-crypto-afsplit test-crypto-block diff --git a/tests/Makefile.include b/tests/Makefile.include index f8e3c6b35a..2010b11b6c 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -22,6 +22,8 @@ check-unit-y += tests/check-qjson$(EXESUF) gcov-files-check-qjson-y = qobject/qjson.c check-unit-y += tests/test-qmp-output-visitor$(EXESUF) gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c +check-unit-y += tests/test-clone-visitor$(EXESUF) +gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c check-unit-y += tests/test-qmp-input-visitor$(EXESUF) gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c check-unit-y += tests/test-qmp-input-strict$(EXESUF) @@ -402,6 +404,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qjson.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \ + tests/test-clone-visitor.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ tests/test-qmp-commands.o tests/test-visitor-serialization.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ @@ -499,6 +502,7 @@ tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $( tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) +tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y) tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c new file mode 100644 index 0000000000..df0c045512 --- /dev/null +++ b/tests/test-clone-visitor.c @@ -0,0 +1,206 @@ +/* + * QAPI Clone Visitor unit-tests. + * + * Copyright (C) 2016 Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "qemu-common.h" +#include "qapi/clone-visitor.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "qapi/qmp/types.h" + +static void test_clone_struct(void) +{ + UserDefOne *src, *dst; + + src = g_new0(UserDefOne, 1); + src->integer = 42; + src->string = g_strdup("Hello"); + src->has_enum1 = false; + src->enum1 = ENUM_ONE_VALUE2; + + dst = QAPI_CLONE(UserDefOne, src); + g_assert(dst); + g_assert_cmpint(dst->integer, ==, 42); + g_assert(dst->string != src->string); + g_assert_cmpstr(dst->string, ==, "Hello"); + g_assert_cmpint(dst->has_enum1, ==, false); + /* Our implementation does this, but it is not required: + g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2); + */ + + qapi_free_UserDefOne(src); + qapi_free_UserDefOne(dst); +} + +static void test_clone_alternate(void) +{ + AltStrBool *b_src, *s_src, *b_dst, *s_dst; + + b_src = g_new0(AltStrBool, 1); + b_src->type = QTYPE_QBOOL; + b_src->u.b = true; + s_src = g_new0(AltStrBool, 1); + s_src->type = QTYPE_QSTRING; + s_src->u.s = g_strdup("World"); + + b_dst = QAPI_CLONE(AltStrBool, b_src); + g_assert(b_dst); + g_assert_cmpint(b_dst->type, ==, b_src->type); + g_assert_cmpint(b_dst->u.b, ==, b_src->u.b); + s_dst = QAPI_CLONE(AltStrBool, s_src); + g_assert(s_dst); + g_assert_cmpint(s_dst->type, ==, s_src->type); + g_assert_cmpstr(s_dst->u.s, ==, s_src->u.s); + g_assert(s_dst->u.s != s_src->u.s); + + qapi_free_AltStrBool(b_src); + qapi_free_AltStrBool(s_src); + qapi_free_AltStrBool(b_dst); + qapi_free_AltStrBool(s_dst); +} + +static void test_clone_native_list(void) +{ + uint8List *src, *dst; + uint8List *tmp = NULL; + int i; + + /* Build list in reverse */ + for (i = 10; i; i--) { + src = g_new0(uint8List, 1); + src->next = tmp; + src->value = i; + tmp = src; + } + + dst = QAPI_CLONE(uint8List, src); + for (tmp = dst, i = 1; i <= 10; i++) { + g_assert(tmp); + g_assert_cmpint(tmp->value, ==, i); + tmp = tmp->next; + } + g_assert(!tmp); + + qapi_free_uint8List(src); + qapi_free_uint8List(dst); +} + +static void test_clone_empty(void) +{ + Empty2 *src, *dst; + + src = g_new0(Empty2, 1); + dst = QAPI_CLONE(Empty2, src); + g_assert(dst); + qapi_free_Empty2(src); + qapi_free_Empty2(dst); +} + +static void test_clone_complex1(void) +{ + UserDefNativeListUnion *src, *dst; + + src = g_new0(UserDefNativeListUnion, 1); + src->type = USER_DEF_NATIVE_LIST_UNION_KIND_STRING; + + dst = QAPI_CLONE(UserDefNativeListUnion, src); + g_assert(dst); + g_assert_cmpint(dst->type, ==, src->type); + g_assert(!dst->u.string.data); + + qapi_free_UserDefNativeListUnion(src); + qapi_free_UserDefNativeListUnion(dst); +} + +static void test_clone_complex2(void) +{ + WrapAlternate *src, *dst; + + src = g_new0(WrapAlternate, 1); + src->alt = g_new(UserDefAlternate, 1); + src->alt->type = QTYPE_QDICT; + src->alt->u.udfu.integer = 42; + /* Clone intentionally converts NULL into "" for strings */ + src->alt->u.udfu.string = NULL; + src->alt->u.udfu.enum1 = ENUM_ONE_VALUE3; + src->alt->u.udfu.u.value3.intb = 99; + src->alt->u.udfu.u.value3.has_a_b = true; + src->alt->u.udfu.u.value3.a_b = true; + + dst = QAPI_CLONE(WrapAlternate, src); + g_assert(dst); + g_assert(dst->alt); + g_assert_cmpint(dst->alt->type, ==, QTYPE_QDICT); + g_assert_cmpint(dst->alt->u.udfu.integer, ==, 42); + g_assert_cmpstr(dst->alt->u.udfu.string, ==, ""); + g_assert_cmpint(dst->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE3); + g_assert_cmpint(dst->alt->u.udfu.u.value3.intb, ==, 99); + g_assert_cmpint(dst->alt->u.udfu.u.value3.has_a_b, ==, true); + g_assert_cmpint(dst->alt->u.udfu.u.value3.a_b, ==, true); + + qapi_free_WrapAlternate(src); + qapi_free_WrapAlternate(dst); +} + +static void test_clone_complex3(void) +{ + __org_qemu_x_Struct2 *src, *dst; + __org_qemu_x_Union1List *tmp; + + src = g_new0(__org_qemu_x_Struct2, 1); + tmp = src->array = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("one"); + tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("two"); + tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("three"); + + dst = QAPI_CLONE(__org_qemu_x_Struct2, src); + g_assert(dst); + tmp = dst->array; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "one"); + tmp = tmp->next; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "two"); + tmp = tmp->next; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "three"); + tmp = tmp->next; + g_assert(!tmp); + + qapi_free___org_qemu_x_Struct2(src); + qapi_free___org_qemu_x_Struct2(dst); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/visitor/clone/struct", test_clone_struct); + g_test_add_func("/visitor/clone/alternate", test_clone_alternate); + g_test_add_func("/visitor/clone/native_list", test_clone_native_list); + g_test_add_func("/visitor/clone/empty", test_clone_empty); + g_test_add_func("/visitor/clone/complex1", test_clone_complex1); + g_test_add_func("/visitor/clone/complex2", test_clone_complex2); + g_test_add_func("/visitor/clone/complex3", test_clone_complex3); + + return g_test_run(); +}