summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Maydell2017-03-23 13:31:52 +0100
committerPeter Maydell2017-03-23 13:31:52 +0100
commitd81d857f4421d205395d55200425daa6591c28a5 (patch)
tree0b383c84ed4a679572bdf7b110809eb97adeca3e
parentMerge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging (diff)
parentqapi: Fix QemuOpts visitor regression on unvisited input (diff)
downloadqemu-d81d857f4421d205395d55200425daa6591c28a5.tar.gz
qemu-d81d857f4421d205395d55200425daa6591c28a5.tar.xz
qemu-d81d857f4421d205395d55200425daa6591c28a5.zip
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-03-22-v3' into staging
QAPI patches for 2017-03-22 # gpg: Signature made Wed 22 Mar 2017 18:25:15 GMT # gpg: using RSA key 0x3870B400EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * remotes/armbru/tags/pull-qapi-2017-03-22-v3: qapi: Fix QemuOpts visitor regression on unvisited input qom: Avoid unvisited 'id'/'qom-type' in user_creatable_add_opts tests: Expose regression in QemuOpts visitor test-qobject-input-visitor: Cover visit_type_uint64() Revert "hostmem: fix QEMU crash by 'info memdev'" qapi: Fix string input visitor regression for empty lists qapi2texi: Fix translation of *strong* and _emphasized_ tests/qapi-schema: Systematic positive doc comment tests tests/qapi-schema: Make test-qapi.py print docs again qapi: Drop unused QAPIDoc member optional qapi2texi: Fix to actually fail when 'doc-required' is false qapi: Drop excessive Make dependencies on qapi2texi.py MAINTAINERS: Add myself for files I touched recently keyval: Document issues with 'any' and alternate types test-keyval: Cover alternate and 'any' type keyval: Improve some comments test-keyval: Tweaks to improve list coverage Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS11
-rw-r--r--Makefile7
-rw-r--r--backends/hostmem.c22
-rw-r--r--qapi/opts-visitor.c6
-rw-r--r--qapi/string-input-visitor.c4
-rw-r--r--qom/object_interfaces.c8
-rw-r--r--scripts/qapi.py1
-rwxr-xr-xscripts/qapi2texi.py5
-rw-r--r--tests/Makefile.include15
-rw-r--r--tests/qapi-schema/doc-good.err0
-rw-r--r--tests/qapi-schema/doc-good.exit1
-rw-r--r--tests/qapi-schema/doc-good.json136
-rw-r--r--tests/qapi-schema/doc-good.out148
-rw-r--r--tests/qapi-schema/doc-good.texi243
-rw-r--r--tests/qapi-schema/test-qapi.py11
-rw-r--r--tests/test-keyval.c59
-rw-r--r--tests/test-opts-visitor.c27
-rw-r--r--tests/test-qobject-input-visitor.c30
-rw-r--r--tests/test-string-input-visitor.c11
-rw-r--r--util/keyval.c57
20 files changed, 747 insertions, 55 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 779c429059..c60235eaf6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1231,6 +1231,15 @@ M: Samuel Thibault <samuel.thibault@ens-lyon.org>
S: Maintained
F: backends/baum.c
+Command line option argument parsing
+M: Markus Armbruster <armbru@redhat.com>
+S: Supported
+F: include/qemu/option.h
+F: tests/test-keyval.c
+F: tests/test-qemu-opts.c
+F: util/keyval.c
+F: util/qemu-option.c
+
Coverity model
M: Markus Armbruster <armbru@redhat.com>
S: Supported
@@ -1365,7 +1374,9 @@ X: include/qapi/qmp/
F: include/qapi/qmp/dispatch.h
F: tests/qapi-schema/
F: tests/test-*-visitor.c
+F: tests/test-qapi-*.c
F: tests/test-qmp-*.c
+F: tests/test-visitor-serialization.c
F: scripts/qapi*
F: docs/qapi*
T: git git://repo.or.cz/qemu/armbru.git qapi-next
diff --git a/Makefile b/Makefile
index f4f90dfad6..6c359b2f86 100644
--- a/Makefile
+++ b/Makefile
@@ -392,7 +392,6 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
gen-out-type = $(subst .,-,$(suffix $@))
qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
-qapi-py += $(SRC_PATH)/scripts/qapi2texi.py
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
@@ -701,10 +700,12 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
-docs/qemu-qmp-qapi.texi: $(qapi-modules) $(qapi-py)
+docs/qemu-qmp-qapi.texi docs/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
+
+docs/qemu-qmp-qapi.texi: $(qapi-modules)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
-docs/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
+docs/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
diff --git a/backends/hostmem.c b/backends/hostmem.c
index 162c2187d8..89feb9ed75 100644
--- a/backends/hostmem.c
+++ b/backends/hostmem.c
@@ -64,14 +64,6 @@ out:
error_propagate(errp, local_err);
}
-static uint16List **host_memory_append_node(uint16List **node,
- unsigned long value)
-{
- *node = g_malloc0(sizeof(**node));
- (*node)->value = value;
- return &(*node)->next;
-}
-
static void
host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
@@ -82,23 +74,25 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
unsigned long value;
value = find_first_bit(backend->host_nodes, MAX_NODES);
-
- node = host_memory_append_node(node, value);
-
if (value == MAX_NODES) {
- goto out;
+ return;
}
+ *node = g_malloc0(sizeof(**node));
+ (*node)->value = value;
+ node = &(*node)->next;
+
do {
value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1);
if (value == MAX_NODES) {
break;
}
- node = host_memory_append_node(node, value);
+ *node = g_malloc0(sizeof(**node));
+ (*node)->value = value;
+ node = &(*node)->next;
} while (true);
-out:
visit_type_uint16List(v, name, &host_nodes, errp);
}
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 026d25b767..324b197495 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -164,7 +164,7 @@ opts_check_struct(Visitor *v, Error **errp)
GHashTableIter iter;
GQueue *any;
- if (ov->depth > 0) {
+ if (ov->depth > 1) {
return;
}
@@ -276,8 +276,8 @@ static void
opts_check_list(Visitor *v, Error **errp)
{
/*
- * FIXME should set error when unvisited elements remain. Mostly
- * harmless, as the generated visits always visit all elements.
+ * Unvisited list elements will be reported later when checking
+ * whether unvisited struct members remain.
*/
}
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 806b01ae3a..c089491c24 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -54,6 +54,10 @@ static int parse_str(StringInputVisitor *siv, const char *name, Error **errp)
return 0;
}
+ if (!*str) {
+ return 0;
+ }
+
do {
errno = 0;
start = strtoll(str, &endptr, 0);
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 03a95c3276..9c271ad32a 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -114,7 +114,7 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
QDict *pdict;
Object *obj;
const char *id = qemu_opts_id(opts);
- const char *type = qemu_opt_get(opts, "qom-type");
+ char *type = qemu_opt_get_del(opts, "qom-type");
if (!type) {
error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
@@ -122,17 +122,19 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
}
if (!id) {
error_setg(errp, QERR_MISSING_PARAMETER, "id");
+ g_free(type);
return NULL;
}
+ qemu_opts_set_id(opts, NULL);
pdict = qemu_opts_to_qdict(opts, NULL);
- qdict_del(pdict, "qom-type");
- qdict_del(pdict, "id");
v = opts_visitor_new(opts);
obj = user_creatable_add_type(type, id, pdict, v, errp);
visit_free(v);
+ qemu_opts_set_id(opts, (char *) id);
+ g_free(type);
QDECREF(pdict);
return obj;
}
diff --git a/scripts/qapi.py b/scripts/qapi.py
index e88c047c2e..6c4d554165 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -106,7 +106,6 @@ class QAPIDoc(object):
self.name = name
# the list of lines for this section
self.content = []
- self.optional = False
def append(self, line):
self.content.append(line)
diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index 8eed11a60c..9e015002ef 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -35,12 +35,12 @@ EXAMPLE_FMT = """@example
def subst_strong(doc):
"""Replaces *foo* by @strong{foo}"""
- return re.sub(r'\*([^*\n]+)\*', r'@emph{\1}', doc)
+ return re.sub(r'\*([^*\n]+)\*', r'@strong{\1}', doc)
def subst_emph(doc):
"""Replaces _foo_ by @emph{foo}"""
- return re.sub(r'\b_([^_\n]+)_\b', r' @emph{\1} ', doc)
+ return re.sub(r'\b_([^_\n]+)_\b', r'@emph{\1}', doc)
def subst_vars(doc):
@@ -292,6 +292,7 @@ def main(argv):
if not qapi.doc_required:
print >>sys.stderr, ("%s: need pragma 'doc-required' "
"to generate documentation" % argv[0])
+ sys.exit(1)
print texi_schema(schema)
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 402e71cf06..f3de81fcfb 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -379,6 +379,7 @@ qapi-schema += doc-duplicated-since.json
qapi-schema += doc-empty-arg.json
qapi-schema += doc-empty-section.json
qapi-schema += doc-empty-symbol.json
+qapi-schema += doc-good.json
qapi-schema += doc-interleaved-section.json
qapi-schema += doc-invalid-end.json
qapi-schema += doc-invalid-end2.json
@@ -607,6 +608,9 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-int
$(gen-out-type) -o tests -p "test-" $<, \
"GEN","$@")
+tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py)
+ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
+
tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y)
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)
@@ -736,7 +740,7 @@ tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o $(test-util-obj-y) \
$(chardev-obj-y)
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
-tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y)
+tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y)
tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y)
tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y)
@@ -856,9 +860,6 @@ QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXE
check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) $(QEMU_IOTESTS_HELPERS-y)
$<
-.PHONY: check-tests/test-qapi.py
-check-tests/test-qapi.py: tests/test-qapi.py
-
.PHONY: $(patsubst %, check-%, $(check-qapi-schema-y))
$(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
$(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts \
@@ -871,10 +872,14 @@ $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
@perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff -q $(SRC_PATH)/$*.err -
@diff -q $(SRC_PATH)/$*.exit $*.test.exit
+.PHONY: check-tests/qapi-schema/doc-good.texi
+check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
+ @diff -q $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
+
# Consolidated targets
.PHONY: check-qapi-schema check-qtest check-unit check check-clean
-check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y))
+check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y)) check-tests/qapi-schema/doc-good.texi
check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
check-unit: $(patsubst %,check-%, $(check-unit-y))
check-block: $(patsubst %,check-%, $(check-block-y))
diff --git a/tests/qapi-schema/doc-good.err b/tests/qapi-schema/doc-good.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/doc-good.err
diff --git a/tests/qapi-schema/doc-good.exit b/tests/qapi-schema/doc-good.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/doc-good.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
new file mode 100644
index 0000000000..cfdc0a8a81
--- /dev/null
+++ b/tests/qapi-schema/doc-good.json
@@ -0,0 +1,136 @@
+# -*- Mode: Python -*-
+# Positive QAPI doc comment tests
+
+{ 'pragma': { 'doc-required': true } }
+
+##
+# = Section
+#
+# == Subsection
+#
+# *strong* _with emphasis_
+# @var {in braces}
+# * List item one
+# - Two, multiple
+# lines
+#
+# 3. Three
+# Still in list
+#
+# Not in list
+# - Second list
+# Note: still in list
+#
+# Note: not in list
+# 1. Third list
+# is numbered
+#
+# - another item
+#
+# | example
+# | multiple lines
+#
+# Returns: the King
+# Since: the first age
+# Notes:
+#
+# 1. Lorem ipsum dolor sit amet
+#
+# 2. Ut enim ad minim veniam
+#
+# Duis aute irure dolor
+#
+# Example:
+#
+# -> in
+# <- out
+# Examples:
+# - *verbatim*
+# - {braces}
+##
+
+##
+# @Enum:
+# == Produces *invalid* texinfo
+# @one: The _one_ {and only}
+#
+# @two is undocumented
+##
+{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
+
+##
+# @Base:
+# @base1:
+# the first member
+##
+{ 'struct': 'Base', 'data': { 'base1': 'Enum' } }
+
+##
+# @Variant1:
+# A paragraph
+#
+# Another paragraph (but no @var: line)
+##
+{ 'struct': 'Variant1', 'data': { 'var1': 'str' } }
+
+##
+# @Variant2:
+##
+{ 'struct': 'Variant2', 'data': {} }
+
+##
+# @Object:
+##
+{ 'union': 'Object',
+ 'base': 'Base',
+ 'discriminator': 'base1',
+ 'data': { 'one': 'Variant1', 'two': 'Variant2' } }
+
+##
+# @SugaredUnion:
+##
+{ 'union': 'SugaredUnion',
+ 'data': { 'one': 'Variant1', 'two': 'Variant2' } }
+
+##
+# == Another subsection
+##
+
+##
+# @cmd:
+# @arg1: the first argument
+#
+# @arg2: the second
+# argument
+# Note: @arg3 is undocumented
+# Returns: @Object
+# TODO: frobnicate
+# Notes:
+# - Lorem ipsum dolor sit amet
+# - Ut enim ad minim veniam
+#
+# Duis aute irure dolor
+# Example:
+#
+# -> in
+# <- out
+# Examples:
+# - *verbatim*
+# - {braces}
+# Since: 2.10
+##
+{ 'command': 'cmd',
+ 'data': { 'arg1': 'int', '*arg2': 'str', 'arg3': 'bool' },
+ 'returns': 'Object' }
+
+##
+# @cmd-boxed:
+# If you're bored enough to read this, go see a video of boxed cats
+# Example:
+#
+# -> in
+#
+# <- out
+##
+{ 'command': 'cmd-boxed', 'boxed': true,
+ 'data': 'Object' }
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
new file mode 100644
index 0000000000..70c1252408
--- /dev/null
+++ b/tests/qapi-schema/doc-good.out
@@ -0,0 +1,148 @@
+object Base
+ member base1: Enum optional=False
+enum Enum ['one', 'two']
+object Object
+ base Base
+ tag base1
+ case one: Variant1
+ case two: Variant2
+enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
+ prefix QTYPE
+object SugaredUnion
+ member type: SugaredUnionKind optional=False
+ tag type
+ case one: q_obj_Variant1-wrapper
+ case two: q_obj_Variant2-wrapper
+enum SugaredUnionKind ['one', 'two']
+object Variant1
+ member var1: str optional=False
+object Variant2
+command cmd q_obj_cmd-arg -> Object
+ gen=True success_response=True boxed=False
+command cmd-boxed Object -> None
+ gen=True success_response=True boxed=True
+object q_empty
+object q_obj_Variant1-wrapper
+ member data: Variant1 optional=False
+object q_obj_Variant2-wrapper
+ member data: Variant2 optional=False
+object q_obj_cmd-arg
+ member arg1: int optional=False
+ member arg2: str optional=True
+ member arg3: bool optional=False
+doc freeform
+ body=
+= Section
+
+== Subsection
+
+*strong* _with emphasis_
+@var {in braces}
+* List item one
+- Two, multiple
+lines
+
+3. Three
+Still in list
+
+Not in list
+- Second list
+Note: still in list
+
+Note: not in list
+1. Third list
+is numbered
+
+- another item
+
+| example
+| multiple lines
+
+Returns: the King
+Since: the first age
+Notes:
+
+1. Lorem ipsum dolor sit amet
+
+2. Ut enim ad minim veniam
+
+Duis aute irure dolor
+
+Example:
+
+-> in
+<- out
+Examples:
+- *verbatim*
+- {braces}
+doc symbol=Enum
+ body=
+== Produces *invalid* texinfo
+ arg=one
+The _one_ {and only}
+ arg=two
+
+ section=
+@two is undocumented
+doc symbol=Base
+ body=
+
+ arg=base1
+the first member
+doc symbol=Variant1
+ body=
+A paragraph
+
+Another paragraph (but no @var: line)
+ arg=var1
+
+doc symbol=Variant2
+ body=
+
+doc symbol=Object
+ body=
+
+doc symbol=SugaredUnion
+ body=
+
+ arg=type
+
+doc freeform
+ body=
+== Another subsection
+doc symbol=cmd
+ body=
+
+ arg=arg1
+the first argument
+ arg=arg2
+the second
+argument
+ arg=arg3
+
+ section=Note
+@arg3 is undocumented
+ section=Returns
+@Object
+ section=TODO
+frobnicate
+ section=Notes
+- Lorem ipsum dolor sit amet
+- Ut enim ad minim veniam
+
+Duis aute irure dolor
+ section=Example
+-> in
+<- out
+ section=Examples
+- *verbatim*
+- {braces}
+ section=Since
+2.10
+doc symbol=cmd-boxed
+ body=
+If you're bored enough to read this, go see a video of boxed cats
+ section=Example
+-> in
+
+<- out
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
new file mode 100644
index 0000000000..c410626e4a
--- /dev/null
+++ b/tests/qapi-schema/doc-good.texi
@@ -0,0 +1,243 @@
+@section Section
+
+@subsection Subsection
+
+@strong{strong} @emph{with emphasis}
+@code{var} @{in braces@}
+@itemize @bullet
+@item
+List item one
+@item
+Two, multiple
+lines
+
+@item
+Three
+Still in list
+
+@end itemize
+
+Not in list
+@itemize @minus
+@item
+Second list
+Note: still in list
+
+@end itemize
+
+Note: not in list
+@enumerate
+@item
+Third list
+is numbered
+
+@item
+another item
+
+@example
+example
+@end example
+
+@example
+multiple lines
+@end example
+
+
+@end enumerate
+
+Returns: the King
+Since: the first age
+Notes:
+
+@enumerate
+@item
+Lorem ipsum dolor sit amet
+
+@item
+Ut enim ad minim veniam
+
+@end enumerate
+
+Duis aute irure dolor
+
+Example:
+
+-> in
+<- out
+Examples:
+@itemize @minus
+@item
+@strong{verbatim}
+@item
+@{braces@}
+@end itemize
+
+
+
+@deftp {Enum} Enum
+
+@subsection Produces @strong{invalid} texinfo
+
+@b{Values:}
+@table @asis
+@item @code{one}
+The @emph{one} @{and only@}
+@item @code{two}
+Not documented
+@end table
+@code{two} is undocumented
+
+@end deftp
+
+
+
+@deftp {Object} Base
+
+
+
+@b{Members:}
+@table @asis
+@item @code{base1: Enum}
+the first member
+@end table
+
+
+@end deftp
+
+
+
+@deftp {Object} Variant1
+
+A paragraph
+
+Another paragraph (but no @code{var}: line)
+
+@b{Members:}
+@table @asis
+@item @code{var1: string}
+Not documented
+@end table
+
+
+@end deftp
+
+
+
+@deftp {Object} Variant2
+
+
+
+
+@end deftp
+
+
+
+@deftp {Object} Object
+
+
+
+@b{Members:}
+@table @asis
+@item The members of @code{Base}
+@item The members of @code{Variant1} when @code{base1} is @t{"one"}
+@item The members of @code{Variant2} when @code{base1} is @t{"two"}
+@end table
+
+
+@end deftp
+
+
+
+@deftp {Object} SugaredUnion
+
+
+
+@b{Members:}
+@table @asis
+@item @code{type}
+One of @t{"one"}, @t{"two"}
+@item @code{data: Variant1} when @code{type} is @t{"one"}
+@item @code{data: Variant2} when @code{type} is @t{"two"}
+@end table
+
+
+@end deftp
+
+
+@subsection Another subsection
+
+
+@deftypefn Command {} cmd
+
+
+
+@b{Arguments:}
+@table @asis
+@item @code{arg1: int}
+the first argument
+@item @code{arg2: string} (optional)
+the second
+argument
+@item @code{arg3: boolean}
+Not documented
+@end table
+
+
+@b{Note:}
+@code{arg3} is undocumented
+
+@b{Returns:}
+@code{Object}
+
+@b{TODO:}
+frobnicate
+
+@b{Notes:}
+@itemize @minus
+@item
+Lorem ipsum dolor sit amet
+@item
+Ut enim ad minim veniam
+
+@end itemize
+
+Duis aute irure dolor
+
+@b{Example:}
+@example
+-> in
+<- out
+@end example
+
+
+@b{Examples:}
+@example
+- *verbatim*
+- @{braces@}
+@end example
+
+
+@b{Since:}
+2.10
+
+@end deftypefn
+
+
+
+@deftypefn Command {} cmd-boxed
+
+If you're bored enough to read this, go see a video of boxed cats
+
+@b{Arguments:} the members of @code{Object}
+
+@b{Example:}
+@example
+-> in
+
+<- out
+@end example
+
+
+@end deftypefn
+
+
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index ef74e2c4c8..c7724d3437 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -55,3 +55,14 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
schema = QAPISchema(sys.argv[1])
schema.visit(QAPISchemaTestVisitor())
+
+for doc in schema.docs:
+ if doc.symbol:
+ print 'doc symbol=%s' % doc.symbol
+ else:
+ print 'doc freeform'
+ print ' body=\n%s' % doc.body
+ for arg, section in doc.args.iteritems():
+ print ' arg=%s\n%s' % (arg, section)
+ for section in doc.sections:
+ print ' section=%s\n%s' % (section.name, section)
diff --git a/tests/test-keyval.c b/tests/test-keyval.c
index 71288b082c..ba19560a22 100644
--- a/tests/test-keyval.c
+++ b/tests/test-keyval.c
@@ -14,6 +14,7 @@
#include "qapi/error.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-input-visitor.h"
+#include "test-qapi-visit.h"
#include "qemu/cutils.h"
#include "qemu/option.h"
@@ -218,14 +219,14 @@ static void test_keyval_parse_list(void)
QDECREF(qdict);
/* Multiple indexes, last one wins */
- qdict = keyval_parse("list.1=goner,list.0=null,list.1=eins,list.2=zwei",
+ qdict = keyval_parse("list.1=goner,list.0=null,list.01=eins,list.2=zwei",
NULL, &error_abort);
g_assert_cmpint(qdict_size(qdict), ==, 1);
check_list012(qdict_get_qlist(qdict, "list"));
QDECREF(qdict);
/* List at deeper nesting */
- qdict = keyval_parse("a.list.1=eins,a.list.0=null,a.list.2=zwei",
+ qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei",
NULL, &error_abort);
g_assert_cmpint(qdict_size(qdict), ==, 1);
sub_qdict = qdict_get_qdict(qdict, "a");
@@ -242,7 +243,7 @@ static void test_keyval_parse_list(void)
g_assert(!qdict);
/* Missing list indexes */
- qdict = keyval_parse("list.2=lonely", NULL, &err);
+ qdict = keyval_parse("list.1=lonely", NULL, &err);
error_free_or_abort(&err);
g_assert(!qdict);
qdict = keyval_parse("list.0=null,list.2=eins,list.02=zwei", NULL, &err);
@@ -608,6 +609,56 @@ static void test_keyval_visit_optional(void)
visit_free(v);
}
+static void test_keyval_visit_alternate(void)
+{
+ Error *err = NULL;
+ Visitor *v;
+ QDict *qdict;
+ AltNumStr *ans;
+ AltNumInt *ani;
+
+ /*
+ * Can't do scalar alternate variants other than string. You get
+ * the string variant if there is one, else an error.
+ */
+ qdict = keyval_parse("a=1,b=2", NULL, &error_abort);
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ QDECREF(qdict);
+ visit_start_struct(v, NULL, NULL, 0, &error_abort);
+ visit_type_AltNumStr(v, "a", &ans, &error_abort);
+ g_assert_cmpint(ans->type, ==, QTYPE_QSTRING);
+ g_assert_cmpstr(ans->u.s, ==, "1");
+ visit_type_AltNumInt(v, "a", &ani, &err);
+ error_free_or_abort(&err);
+ visit_end_struct(v, NULL);
+ visit_free(v);
+}
+
+static void test_keyval_visit_any(void)
+{
+ Visitor *v;
+ QDict *qdict;
+ QObject *any;
+ QList *qlist;
+ QString *qstr;
+
+ qdict = keyval_parse("a.0=null,a.1=1", NULL, &error_abort);
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ QDECREF(qdict);
+ visit_start_struct(v, NULL, NULL, 0, &error_abort);
+ visit_type_any(v, "a", &any, &error_abort);
+ qlist = qobject_to_qlist(any);
+ g_assert(qlist);
+ qstr = qobject_to_qstring(qlist_pop(qlist));
+ g_assert_cmpstr(qstring_get_str(qstr), ==, "null");
+ qstr = qobject_to_qstring(qlist_pop(qlist));
+ g_assert_cmpstr(qstring_get_str(qstr), ==, "1");
+ g_assert(qlist_empty(qlist));
+ visit_check_struct(v, &error_abort);
+ visit_end_struct(v, NULL);
+ visit_free(v);
+}
+
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
@@ -619,6 +670,8 @@ int main(int argc, char *argv[])
g_test_add_func("/keyval/visit/dict", test_keyval_visit_dict);
g_test_add_func("/keyval/visit/list", test_keyval_visit_list);
g_test_add_func("/keyval/visit/optional", test_keyval_visit_optional);
+ g_test_add_func("/keyval/visit/alternate", test_keyval_visit_alternate);
+ g_test_add_func("/keyval/visit/any", test_keyval_visit_any);
g_test_run();
return 0;
}
diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
index 2238f8efe5..23e897061c 100644
--- a/tests/test-opts-visitor.c
+++ b/tests/test-opts-visitor.c
@@ -175,6 +175,7 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data)
static void
test_opts_range_unvisited(void)
{
+ Error *err = NULL;
intList *list = NULL;
intList *tail;
QemuOpts *opts;
@@ -199,10 +200,11 @@ test_opts_range_unvisited(void)
g_assert_cmpint(tail->value, ==, 1);
tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*list));
g_assert(tail);
- visit_check_list(v, &error_abort); /* BUG: unvisited tail not reported */
+ visit_check_list(v, &error_abort); /* unvisited tail ignored until... */
visit_end_list(v, (void **)&list);
- visit_check_struct(v, &error_abort);
+ visit_check_struct(v, &err); /* ...here */
+ error_free_or_abort(&err);
visit_end_struct(v, NULL);
qapi_free_intList(list);
@@ -247,6 +249,25 @@ test_opts_range_beyond(void)
qemu_opts_del(opts);
}
+static void
+test_opts_dict_unvisited(void)
+{
+ Error *err = NULL;
+ QemuOpts *opts;
+ Visitor *v;
+ UserDefOptions *userdef;
+
+ opts = qemu_opts_parse(qemu_find_opts("userdef"), "i64x=0,bogus=1", false,
+ &error_abort);
+
+ v = opts_visitor_new(opts);
+ visit_type_UserDefOptions(v, NULL, &userdef, &err);
+ error_free_or_abort(&err);
+ visit_free(v);
+ qemu_opts_del(opts);
+ g_assert(!userdef);
+}
+
int
main(int argc, char **argv)
{
@@ -343,6 +364,8 @@ main(int argc, char **argv)
g_test_add_func("/visitor/opts/range/beyond",
test_opts_range_beyond);
+ g_test_add_func("/visitor/opts/dict/unvisited", test_opts_dict_unvisited);
+
g_test_run();
return 0;
}
diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
index 6eb48fee7b..f965743b6e 100644
--- a/tests/test-qobject-input-visitor.c
+++ b/tests/test-qobject-input-visitor.c
@@ -116,6 +116,34 @@ static void test_visitor_in_int(TestInputVisitorData *data,
g_assert_cmpint(res, ==, value);
}
+static void test_visitor_in_uint(TestInputVisitorData *data,
+ const void *unused)
+{
+ Error *err = NULL;
+ uint64_t res = 0;
+ int value = 42;
+ Visitor *v;
+
+ v = visitor_input_test_init(data, "%d", value);
+
+ visit_type_uint64(v, NULL, &res, &error_abort);
+ g_assert_cmpuint(res, ==, (uint64_t)value);
+
+ /* BUG: value between INT64_MIN and -1 accepted modulo 2^64 */
+
+ v = visitor_input_test_init(data, "%d", -value);
+
+ visit_type_uint64(v, NULL, &res, &error_abort);
+ g_assert_cmpuint(res, ==, (uint64_t)-value);
+
+ /* BUG: value between INT64_MAX+1 and UINT64_MAX rejected */
+
+ v = visitor_input_test_init(data, "18446744073709551574");
+
+ visit_type_uint64(v, NULL, &res, &err);
+ error_free_or_abort(&err);
+}
+
static void test_visitor_in_int_overflow(TestInputVisitorData *data,
const void *unused)
{
@@ -1225,6 +1253,8 @@ int main(int argc, char **argv)
input_visitor_test_add("/visitor/input/int",
NULL, test_visitor_in_int);
+ input_visitor_test_add("/visitor/input/uint",
+ NULL, test_visitor_in_uint);
input_visitor_test_add("/visitor/input/int_overflow",
NULL, test_visitor_in_int_overflow);
input_visitor_test_add("/visitor/input/int_keyval",
diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index 6db850bc89..79313a7f7a 100644
--- a/tests/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
@@ -63,6 +63,11 @@ static void test_visitor_in_int(TestInputVisitorData *data,
visit_type_int(v, NULL, &res, &err);
error_free_or_abort(&err);
+
+ v = visitor_input_test_init(data, "");
+
+ visit_type_int(v, NULL, &res, &err);
+ error_free_or_abort(&err);
}
static void check_ilist(Visitor *v, int64_t *expected, size_t n)
@@ -140,11 +145,11 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
v = visitor_input_test_init(data, "18446744073709551615");
check_ulist(v, expect4, ARRAY_SIZE(expect4));
- /* Empty list is invalid (weird) */
+ /* Empty list */
v = visitor_input_test_init(data, "");
- visit_type_int64List(v, NULL, &res, &err);
- error_free_or_abort(&err);
+ visit_type_int64List(v, NULL, &res, &error_abort);
+ g_assert(!res);
/* Not a list */
diff --git a/util/keyval.c b/util/keyval.c
index f646b36821..93d5db6b59 100644
--- a/util/keyval.c
+++ b/util/keyval.c
@@ -21,22 +21,36 @@
*
* Semantics defined by reduction to JSON:
*
- * key-vals is a tree of objects and arrays rooted at object R
- * where for each key-val = key-fragment . ... = val in key-vals
- * R op key-fragment op ... = val'
- * where (left-associative) op is
- * array subscript L[key-fragment] for numeric key-fragment
- * member reference L.key-fragment otherwise
- * val' is val with ',,' replaced by ','
- * and only R may be empty.
+ * key-vals specifies a JSON object, i.e. a tree whose root is an
+ * object, inner nodes other than the root are objects or arrays,
+ * and leaves are strings.
*
- * Duplicate keys are permitted; all but the last one are ignored.
+ * Each key-val = key-fragment '.' ... '=' val specifies a path from
+ * root to a leaf (left of '='), and the leaf's value (right of
+ * '=').
*
- * The equations must have a solution. Counter-example: a.b=1,a=2
- * doesn't have one, because R.a must be an object to satisfy a.b=1
- * and a string to satisfy a=2.
+ * A path from the root is defined recursively:
+ * L '.' key-fragment is a child of the node denoted by path L
+ * key-fragment is a child of the tree root
+ * If key-fragment is numeric, the parent is an array and the child
+ * is its key-fragment-th member, counting from zero.
+ * Else, the parent is an object, and the child is its member named
+ * key-fragment.
*
- * Key-fragments must be valid QAPI names or consist only of digits.
+ * This constrains inner nodes to be either array or object. The
+ * constraints must be satisfiable. Counter-example: a.b=1,a=2 is
+ * not, because root.a must be an object to satisfy a.b=1 and a
+ * string to satisfy a=2.
+ *
+ * Array subscripts can occur in any order, but the set of
+ * subscripts must not have gaps. For instance, a.1=v is not okay,
+ * because root.a[0] is missing.
+ *
+ * If multiple key-val denote the same leaf, the last one determines
+ * the value.
+ *
+ * Key-fragments must be valid QAPI names or consist only of decimal
+ * digits.
*
* The length of any key-fragment must be between 1 and 127.
*
@@ -47,6 +61,16 @@
* "key absent" already means "optional object/array absent", which
* isn't the same as "empty object/array present".
*
+ * Design flaw: scalar values can only be strings; there is no way to
+ * denote numbers, true, false or null. The special QObject input
+ * visitor returned by qobject_input_visitor_new_keyval() mostly hides
+ * this by automatically converting strings to the type the visitor
+ * expects. Breaks down for alternate types and type 'any', where the
+ * visitor's expectation isn't clear. Code visiting such types needs
+ * to do the conversion itself, but only when using this keyval
+ * visitor. Awkward. Alternate types without a string member don't
+ * work at all.
+ *
* Additional syntax for use with an implied key:
*
* key-vals-ik = val-no-key [ ',' key-vals ]
@@ -64,8 +88,8 @@
/*
* Convert @key to a list index.
- * Convert all leading digits to a (non-negative) number, capped at
- * INT_MAX.
+ * Convert all leading decimal digits to a (non-negative) number,
+ * capped at INT_MAX.
* If @end is non-null, assign a pointer to the first character after
* the number to *@end.
* Else, fail if any characters follow.
@@ -337,7 +361,8 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp)
}
/*
- * Make a list from @elt[], reporting any missing elements.
+ * Make a list from @elt[], reporting the first missing element,
+ * if any.
* If we dropped an index >= nelt in the previous loop, this loop
* will run into the sentinel and report index @nelt missing.
*/