From nobody Fri Nov 7 03:56:42 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1545598950765477.1576666653425; Sun, 23 Dec 2018 13:02:30 -0800 (PST) Received: from localhost ([127.0.0.1]:42214 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gbAtJ-00052M-6r for importer@patchew.org; Sun, 23 Dec 2018 16:02:29 -0500 Received: from eggs.gnu.org ([208.118.235.92]:52041) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gbAjk-0007MA-C3 for qemu-devel@nongnu.org; Sun, 23 Dec 2018 15:52:38 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gbAji-0001Vn-45 for qemu-devel@nongnu.org; Sun, 23 Dec 2018 15:52:36 -0500 Received: from mail-wr1-x444.google.com ([2a00:1450:4864:20::444]:38255) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gbAjh-0001R3-Pj for qemu-devel@nongnu.org; Sun, 23 Dec 2018 15:52:34 -0500 Received: by mail-wr1-x444.google.com with SMTP id v13so10103041wrw.5 for ; Sun, 23 Dec 2018 12:52:33 -0800 (PST) Received: from nullptr.home.dirty-ice.org (2a01-036c-0113-24a3-0000-0000-0000-0005.pool6.digikabel.hu. [2a01:36c:113:24a3::5]) by smtp.gmail.com with ESMTPSA id g198sm25456920wmd.23.2018.12.23.12.52.31 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 23 Dec 2018 12:52:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=m94Go48ZwqUBlRLMY8hU2u+ONGMXkfsC28xGxzVnpO8=; b=bA3riAv7TsIbgGry0MYu0GCNjUXgk1HkR3dnGvBTrEqyE6q4ZSwpHh8MXP2z5IdgH9 +rOhS/T3T5ncyGINrJ5SAl//Pl2ak4EayVT4TKDC5QLpjGD3G8XbsF5W24SLrB2arSPw JeovXQoshm+jhBVugp9HgiSt8EcE3yY3DI6rSGKDiv7K9eBTQrpwsWWkd5AH+6YwmvLM jehcQV653kzXmtEC78x1mOwc8AFdh0AG+kF7FmLhqfesVBFWFfelH+zLRgT/13bARfLp MALVGitkNq0C2s9UT9FatHVeQSEJJGh2Zal7G/KGODX1MZIKeKZx1RRsWcs1VRMI602J 5aoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=m94Go48ZwqUBlRLMY8hU2u+ONGMXkfsC28xGxzVnpO8=; b=PdJBR9jRx6QsbY2qUaYpGTDkpD0bHj+BmfD24l5wAm1/+UZtmosOCxyYET05j+RHtj fjXi4f5d4ay3zgRDtdOQA1yB2IzOh8wEXsIj7rjpswbum8pwM+3DJWLa5lk2lGy8gQT/ OYjhkRLdd26dS4EtEwJybdpZmhH8JQDmE/OcQVx0ukAX59JlxzQoFBE3QIX5IibT2XZv r2VlEYL86p3O8MdCFWSvbMqFR6GBnKEnc1mS2Z0EEomKzbWEgVNgszQmJgGZT1q36vBJ RwezYncs2HukIn6J0sskuplRRFNJa05rxOCGkoukvLFtAg1ACLd9QOPvSQ8qpgnrLl7C 0WYw== X-Gm-Message-State: AJcUukegHD2ZOraOSyav1Pu/mDDcYFT5drV54Xnbb/ZyCaESDK0wUp80 agRdbj7GeoYa6ie2E5jn68XVRLmzUb8= X-Google-Smtp-Source: ALg8bN785B6f+VyM3AeAhtPSf+sf21KcrEXIlDM7RxllrJuWNRW/SHIbjKTJit3/TKLy2JNb+bmdIw== X-Received: by 2002:a5d:4b01:: with SMTP id v1mr9205935wrq.5.1545598352368; Sun, 23 Dec 2018 12:52:32 -0800 (PST) From: "=?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?=" X-Google-Original-From: =?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?= To: qemu-devel@nongnu.org Date: Sun, 23 Dec 2018 21:51:38 +0100 Message-Id: <8b272e5a75fa89e5acaa7ae9aa3a8260de6744db.1545598229.git.DirtY.iCE.hu@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::444 Subject: [Qemu-devel] [PATCH v2 02/52] qapi: support nested structs in OptsVisitor X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Eduardo Habkost , "Michael S. Tsirkin" , Jason Wang , Michael Roth , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?= , Igor Mammedov , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) From: K=C5=91v=C3=A1g=C3=B3, Zolt=C3=A1n The current OptsVisitor flattens the whole structure, if there are same named fields under different paths (like `in' and `out' in `Audiodev'), the current visitor can't cope with them (for example setting `frequency=3D44100' will set the in's frequency to 44100 and leave out's frequency unspecified). This patch fixes it, by always requiring a complete path in case of nested structs. Fields in the path are separated by dots, similar to C structs (without pointers), like `in.frequency' or `out.frequency'. You must provide a full path even in non-ambiguous cases. To keep backward compatibility, this new feature can be disabled when creating a new OptsVisitor, in that case it will work identical to previous versions. Signed-off-by: K=C5=91v=C3=A1g=C3=B3, Zolt=C3=A1n --- hw/acpi/core.c | 2 +- include/qapi/opts-visitor.h | 2 +- net/net.c | 2 +- numa.c | 2 +- qapi/opts-visitor.c | 129 ++++++++++++++++++++---- qom/object_interfaces.c | 2 +- tests/qapi-schema/qapi-schema-test.json | 9 +- tests/qapi-schema/qapi-schema-test.out | 4 + tests/test-opts-visitor.c | 43 +++++++- 9 files changed, 163 insertions(+), 32 deletions(-) diff --git a/hw/acpi/core.c b/hw/acpi/core.c index d6f0709691..654508ac13 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -243,7 +243,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) { Visitor *v; =20 - v =3D opts_visitor_new(opts); + v =3D opts_visitor_new(opts, false); visit_type_AcpiTableOptions(v, NULL, &hdrs, &err); visit_free(v); } diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h index ca044e3b33..84edb49f7a 100644 --- a/include/qapi/opts-visitor.h +++ b/include/qapi/opts-visitor.h @@ -33,6 +33,6 @@ typedef struct OptsVisitor OptsVisitor; * (other than integers), null, or arbitrary QTypes. It also requires a * non-null list argument to visit_start_list(). */ -Visitor *opts_visitor_new(const QemuOpts *opts); +Visitor *opts_visitor_new(const QemuOpts *opts, bool nested); =20 #endif diff --git a/net/net.c b/net/net.c index 1f7d626197..f5e7d8a6ef 100644 --- a/net/net.c +++ b/net/net.c @@ -1106,7 +1106,7 @@ static int net_client_init(QemuOpts *opts, bool is_ne= tdev, Error **errp) void *object =3D NULL; Error *err =3D NULL; int ret =3D -1; - Visitor *v =3D opts_visitor_new(opts); + Visitor *v =3D opts_visitor_new(opts, false); =20 const char *type =3D qemu_opt_get(opts, "type"); =20 diff --git a/numa.c b/numa.c index 50ec016013..31a24f750f 100644 --- a/numa.c +++ b/numa.c @@ -220,7 +220,7 @@ static int parse_numa(void *opaque, QemuOpts *opts, Err= or **errp) NumaOptions *object =3D NULL; MachineState *ms =3D MACHINE(opaque); Error *err =3D NULL; - Visitor *v =3D opts_visitor_new(opts); + Visitor *v =3D opts_visitor_new(opts, false); =20 visit_type_NumaOptions(v, NULL, &object, &err); visit_free(v); diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 4af6043b75..d8af51b16c 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -72,6 +72,7 @@ struct OptsVisitor * schema, with a single mandatory scalar member. */ ListMode list_mode; GQueue *repeated_opts; + char *repeated_name; =20 /* When parsing a list of repeating options as integers, values of the= form * "a-b", representing a closed interval, are allowed. Elements in the @@ -87,6 +88,9 @@ struct OptsVisitor * not survive or escape the OptsVisitor object. */ QemuOpt *fake_id_opt; + + /* List of field names leading to the current structure. */ + GQueue *nested_names; }; =20 =20 @@ -107,6 +111,7 @@ static void opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) { GQueue *list; + assert(opt); =20 list =3D g_hash_table_lookup(unprocessed_opts, opt->name); if (list =3D=3D NULL) { @@ -134,6 +139,11 @@ opts_start_struct(Visitor *v, const char *name, void *= *obj, if (obj) { *obj =3D g_malloc0(size); } + + if (ov->nested_names !=3D NULL) { + g_queue_push_tail(ov->nested_names, (gpointer) name); + } + if (ov->depth++ > 0) { return; } @@ -171,6 +181,10 @@ opts_check_struct(Visitor *v, Error **errp) GHashTableIter iter; GQueue *any; =20 + if (ov->nested_names !=3D NULL) { + g_queue_pop_tail(ov->nested_names); + } + if (ov->depth > 1) { return; } @@ -212,15 +226,59 @@ opts_end_alternate(Visitor *v, void **obj) } =20 =20 +static void +sum_strlen(gpointer data, gpointer user_data) +{ + const char *str =3D data; + size_t *sum_len =3D user_data; + + if (str) { /* skip NULLs */ + *sum_len +=3D strlen(str) + 1; + } +} + +static void +append_str(gpointer data, gpointer user_data) +{ + const char *str =3D data; + char *concat_str =3D user_data; + + if (str) { + strcat(concat_str, str); + strcat(concat_str, "."); + } +} + +/* lookup a name, using a fully qualified version */ static GQueue * -lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) +lookup_distinct(const OptsVisitor *ov, const char *name, char **out_key, + Error **errp) { - GQueue *list; + GQueue *list =3D NULL; + char *key; + + if (ov->nested_names !=3D NULL) { + size_t sum_len =3D strlen(name); + g_queue_foreach(ov->nested_names, sum_strlen, &sum_len); + key =3D g_malloc(sum_len + 1); + key[0] =3D 0; + g_queue_foreach(ov->nested_names, append_str, key); + strcat(key, name); + } else { + key =3D g_strdup(name); + } + + list =3D g_hash_table_lookup(ov->unprocessed_opts, key); + if (list && out_key) { + *out_key =3D key; + key =3D NULL; + } =20 - list =3D g_hash_table_lookup(ov->unprocessed_opts, name); if (!list) { error_setg(errp, QERR_MISSING_PARAMETER, name); } + + g_free(key); return list; } =20 @@ -235,7 +293,8 @@ opts_start_list(Visitor *v, const char *name, GenericLi= st **list, size_t size, assert(ov->list_mode =3D=3D LM_NONE); /* we don't support visits without a list */ assert(list); - ov->repeated_opts =3D lookup_distinct(ov, name, errp); + assert(ov->repeated_opts =3D=3D NULL && ov->repeated_name =3D=3D NULL); + ov->repeated_opts =3D lookup_distinct(ov, name, &ov->repeated_name, er= rp); if (ov->repeated_opts) { ov->list_mode =3D LM_IN_PROGRESS; *list =3D g_malloc0(size); @@ -266,11 +325,9 @@ opts_next_list(Visitor *v, GenericList *tail, size_t s= ize) /* range has been completed, fall through in order to pop option */ =20 case LM_IN_PROGRESS: { - const QemuOpt *opt; - - opt =3D g_queue_pop_head(ov->repeated_opts); + g_queue_pop_head(ov->repeated_opts); if (g_queue_is_empty(ov->repeated_opts)) { - g_hash_table_remove(ov->unprocessed_opts, opt->name); + g_hash_table_remove(ov->unprocessed_opts, ov->repeated_name); return NULL; } break; @@ -304,22 +361,28 @@ opts_end_list(Visitor *v, void **obj) ov->list_mode =3D=3D LM_SIGNED_INTERVAL || ov->list_mode =3D=3D LM_UNSIGNED_INTERVAL); ov->repeated_opts =3D NULL; + + g_free(ov->repeated_name); + ov->repeated_name =3D NULL; + ov->list_mode =3D LM_NONE; } =20 =20 static const QemuOpt * -lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp) +lookup_scalar(const OptsVisitor *ov, const char *name, char** out_key, + Error **errp) { if (ov->list_mode =3D=3D LM_NONE) { GQueue *list; =20 /* the last occurrence of any QemuOpt takes effect when queried by= name */ - list =3D lookup_distinct(ov, name, errp); + list =3D lookup_distinct(ov, name, out_key, errp); return list ? g_queue_peek_tail(list) : NULL; } assert(ov->list_mode =3D=3D LM_IN_PROGRESS); + assert(out_key =3D=3D NULL || *out_key =3D=3D NULL); return g_queue_peek_head(ov->repeated_opts); } =20 @@ -341,8 +404,9 @@ opts_type_str(Visitor *v, const char *name, char **obj,= Error **errp) { OptsVisitor *ov =3D to_ov(v); const QemuOpt *opt; + char *key =3D NULL; =20 - opt =3D lookup_scalar(ov, name, errp); + opt =3D lookup_scalar(ov, name, &key, errp); if (!opt) { *obj =3D NULL; return; @@ -353,7 +417,8 @@ opts_type_str(Visitor *v, const char *name, char **obj,= Error **errp) * valid enum value; this is harmless because tracking what gets * consumed only matters to visit_end_struct() as the final error * check if there were no other failures during the visit. */ - processed(ov, name); + processed(ov, key); + g_free(key); } =20 =20 @@ -363,8 +428,9 @@ opts_type_bool(Visitor *v, const char *name, bool *obj,= Error **errp) { OptsVisitor *ov =3D to_ov(v); const QemuOpt *opt; + char *key =3D NULL; =20 - opt =3D lookup_scalar(ov, name, errp); + opt =3D lookup_scalar(ov, name, &key, errp); if (!opt) { return; } @@ -381,13 +447,15 @@ opts_type_bool(Visitor *v, const char *name, bool *ob= j, Error **errp) } else { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, "on|yes|y|off|no|n"); + g_free(key); return; } } else { *obj =3D true; } =20 - processed(ov, name); + processed(ov, key); + g_free(key); } =20 =20 @@ -399,13 +467,14 @@ opts_type_int64(Visitor *v, const char *name, int64_t= *obj, Error **errp) const char *str; long long val; char *endptr; + char *key =3D NULL; =20 if (ov->list_mode =3D=3D LM_SIGNED_INTERVAL) { *obj =3D ov->range_next.s; return; } =20 - opt =3D lookup_scalar(ov, name, errp); + opt =3D lookup_scalar(ov, name, &key, errp); if (!opt) { return; } @@ -419,11 +488,13 @@ opts_type_int64(Visitor *v, const char *name, int64_t= *obj, Error **errp) if (errno =3D=3D 0 && endptr > str && INT64_MIN <=3D val && val <=3D I= NT64_MAX) { if (*endptr =3D=3D '\0') { *obj =3D val; - processed(ov, name); + processed(ov, key); + g_free(key); return; } if (*endptr =3D=3D '-' && ov->list_mode =3D=3D LM_IN_PROGRESS) { long long val2; + assert(key =3D=3D NULL); =20 str =3D endptr + 1; val2 =3D strtoll(str, &endptr, 0); @@ -444,6 +515,7 @@ opts_type_int64(Visitor *v, const char *name, int64_t *= obj, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, (ov->list_mode =3D=3D LM_NONE) ? "an int64 value" : "an int64 value or range"); + g_free(key); } =20 =20 @@ -455,13 +527,14 @@ opts_type_uint64(Visitor *v, const char *name, uint64= _t *obj, Error **errp) const char *str; unsigned long long val; char *endptr; + char *key =3D NULL; =20 if (ov->list_mode =3D=3D LM_UNSIGNED_INTERVAL) { *obj =3D ov->range_next.u; return; } =20 - opt =3D lookup_scalar(ov, name, errp); + opt =3D lookup_scalar(ov, name, &key, errp); if (!opt) { return; } @@ -473,11 +546,13 @@ opts_type_uint64(Visitor *v, const char *name, uint64= _t *obj, Error **errp) if (parse_uint(str, &val, &endptr, 0) =3D=3D 0 && val <=3D UINT64_MAX)= { if (*endptr =3D=3D '\0') { *obj =3D val; - processed(ov, name); + processed(ov, key); + g_free(key); return; } if (*endptr =3D=3D '-' && ov->list_mode =3D=3D LM_IN_PROGRESS) { unsigned long long val2; + assert(key =3D=3D NULL); =20 str =3D endptr + 1; if (parse_uint_full(str, &val2, 0) =3D=3D 0 && @@ -496,6 +571,7 @@ opts_type_uint64(Visitor *v, const char *name, uint64_t= *obj, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, (ov->list_mode =3D=3D LM_NONE) ? "a uint64 value" : "a uint64 value or range"); + g_free(key); } =20 =20 @@ -505,8 +581,9 @@ opts_type_size(Visitor *v, const char *name, uint64_t *= obj, Error **errp) OptsVisitor *ov =3D to_ov(v); const QemuOpt *opt; int err; + char *key =3D NULL; =20 - opt =3D lookup_scalar(ov, name, errp); + opt =3D lookup_scalar(ov, name, &key, errp); if (!opt) { return; } @@ -515,10 +592,12 @@ opts_type_size(Visitor *v, const char *name, uint64_t= *obj, Error **errp) if (err < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, "a size value"); + g_free(key); return; } =20 - processed(ov, name); + processed(ov, key); + g_free(key); } =20 =20 @@ -529,7 +608,7 @@ opts_optional(Visitor *v, const char *name, bool *prese= nt) =20 /* we only support a single mandatory scalar field in a list node */ assert(ov->list_mode =3D=3D LM_NONE); - *present =3D (lookup_distinct(ov, name, NULL) !=3D NULL); + *present =3D (lookup_distinct(ov, name, NULL, NULL) !=3D NULL); } =20 =20 @@ -547,7 +626,7 @@ opts_free(Visitor *v) =20 =20 Visitor * -opts_visitor_new(const QemuOpts *opts) +opts_visitor_new(const QemuOpts *opts, bool nested) { OptsVisitor *ov; =20 @@ -556,6 +635,12 @@ opts_visitor_new(const QemuOpts *opts) =20 ov->visitor.type =3D VISITOR_INPUT; =20 + if (nested) { + ov->nested_names =3D g_queue_new(); + } else { + ov->nested_names =3D NULL; + } + ov->visitor.start_struct =3D &opts_start_struct; ov->visitor.check_struct =3D &opts_check_struct; ov->visitor.end_struct =3D &opts_end_struct; diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index db85d1eb75..17f995ee31 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -119,7 +119,7 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error *= *errp) qemu_opts_set_id(opts, NULL); pdict =3D qemu_opts_to_qdict(opts, NULL); =20 - v =3D opts_visitor_new(opts); + v =3D opts_visitor_new(opts, false); obj =3D user_creatable_add_type(type, id, pdict, v, errp); visit_free(v); =20 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qa= pi-schema-test.json index cb0857df52..324b9eb40c 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -149,6 +149,11 @@ # Smoke test on out-of-band and allow-preconfig-test { 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': t= rue } =20 +# For testing hierarchy support in opts-visitor +{ 'struct': 'UserDefOptionsSub', + 'data': { + '*nint': 'int' } } + # For testing integer range flattening in opts-visitor. The following sche= ma # corresponds to the option format: # @@ -162,7 +167,9 @@ '*u64' : [ 'uint64' ], '*u16' : [ 'uint16' ], '*i64x': 'int' , - '*u64x': 'uint64' } } + '*u64x': 'uint64' , + 'sub0': 'UserDefOptionsSub', + 'sub1': 'UserDefOptionsSub' } } =20 # testing event { 'struct': 'EventStructOne', diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qap= i-schema-test.out index 9464101d26..3c1b47c7cd 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -200,12 +200,16 @@ command boxed-union UserDefNativeListUnion -> None gen=3DTrue success_response=3DTrue boxed=3DTrue oob=3DFalse preconfig= =3DFalse command test-flags-command None -> None gen=3DTrue success_response=3DTrue boxed=3DFalse oob=3DTrue preconfig= =3DTrue +object UserDefOptionsSub + member nint: int optional=3DTrue object UserDefOptions member i64: intList optional=3DTrue member u64: uint64List optional=3DTrue member u16: uint16List optional=3DTrue member i64x: int optional=3DTrue member u64x: uint64 optional=3DTrue + member sub0: UserDefOptionsSub optional=3DFalse + member sub1: UserDefOptionsSub optional=3DFalse object EventStructOne member struct1: UserDefOne optional=3DFalse member string: str optional=3DFalse diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c index 23e897061c..52950dfc4a 100644 --- a/tests/test-opts-visitor.c +++ b/tests/test-opts-visitor.c @@ -43,7 +43,7 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_d= ata) NULL); g_assert(opts !=3D NULL); =20 - v =3D opts_visitor_new(opts); + v =3D opts_visitor_new(opts, true); visit_type_UserDefOptions(v, NULL, &f->userdef, &f->err); visit_free(v); qemu_opts_del(opts); @@ -170,6 +170,34 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer te= st_data) g_assert(f->userdef->u64->value =3D=3D UINT64_MAX); } =20 +static void +expect_both(OptsVisitorFixture *f, gconstpointer test_data) +{ + expect_ok(f, test_data); + g_assert(f->userdef->sub0->has_nint); + g_assert(f->userdef->sub0->nint =3D=3D 13); + g_assert(f->userdef->sub1->has_nint); + g_assert(f->userdef->sub1->nint =3D=3D 17); +} + +static void +expect_sub0(OptsVisitorFixture *f, gconstpointer test_data) +{ + expect_ok(f, test_data); + g_assert(f->userdef->sub0->has_nint); + g_assert(f->userdef->sub0->nint =3D=3D 13); + g_assert(!f->userdef->sub1->has_nint); +} + +static void +expect_sub1(OptsVisitorFixture *f, gconstpointer test_data) +{ + expect_ok(f, test_data); + g_assert(!f->userdef->sub0->has_nint); + g_assert(f->userdef->sub1->has_nint); + g_assert(f->userdef->sub1->nint =3D=3D 13); +} + /* test cases */ =20 static void @@ -184,7 +212,7 @@ test_opts_range_unvisited(void) opts =3D qemu_opts_parse(qemu_find_opts("userdef"), "ilist=3D0-2", fal= se, &error_abort); =20 - v =3D opts_visitor_new(opts); + v =3D opts_visitor_new(opts, false); =20 visit_start_struct(v, NULL, NULL, 0, &error_abort); =20 @@ -225,7 +253,7 @@ test_opts_range_beyond(void) opts =3D qemu_opts_parse(qemu_find_opts("userdef"), "ilist=3D0", false, &error_abort); =20 - v =3D opts_visitor_new(opts); + v =3D opts_visitor_new(opts, false); =20 visit_start_struct(v, NULL, NULL, 0, &error_abort); =20 @@ -260,7 +288,7 @@ test_opts_dict_unvisited(void) opts =3D qemu_opts_parse(qemu_find_opts("userdef"), "i64x=3D0,bogus=3D= 1", false, &error_abort); =20 - v =3D opts_visitor_new(opts); + v =3D opts_visitor_new(opts, false); visit_type_UserDefOptions(v, NULL, &userdef, &err); error_free_or_abort(&err); visit_free(v); @@ -366,6 +394,13 @@ main(int argc, char **argv) =20 g_test_add_func("/visitor/opts/dict/unvisited", test_opts_dict_unvisit= ed); =20 + /* Test nested structs support */ + add_test("/visitor/opts/nested/unqualified", &expect_fail, "nint=3D13"= ); + add_test("/visitor/opts/nested/both", &expect_both, + "sub0.nint=3D13,sub1.nint=3D17"); + add_test("/visitor/opts/nested/sub0", &expect_sub0, "sub0.nint= =3D13"); + add_test("/visitor/opts/nested/sub1", &expect_sub1, "sub1.nint= =3D13"); + g_test_run(); return 0; } --=20 2.20.1