aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-05-05 18:22:12 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-05-05 18:22:12 +0100
commit233353ec93e4541fa7ab1c53a922a6d5c2bfce7a (patch)
tree39ff570f5e091ca43323e7270f80bb9176a9907f
parent874e9aeeeb74c5459639a93439a502d262847e68 (diff)
parentff55d72eaf9628e7d58e7b067b361cdbf789c9f4 (diff)
Merge remote-tracking branch 'remotes/armbru/tags/pull-qmp-2015-05-05' into staging
drop qapi nested structs # gpg: Signature made Tue May 5 17:40:40 2015 BST using RSA key ID EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" * remotes/armbru/tags/pull-qmp-2015-05-05: (40 commits) qapi: Check for member name conflicts with a base class qapi: Support (subset of) \u escapes in strings qapi: Tweak doc references to QMP when QGA is also meant qapi: Drop dead visitor code related to nested structs qapi: Drop support for inline nested types qapi: Drop inline nested structs in query-pci qapi: Drop inline nested struct in query-version qapi: Drop tests for inline nested structs qapi: Merge UserDefTwo and UserDefNested in tests qapi: Forbid 'type' in schema qapi: Use 'struct' instead of 'type' in schema qapi: Document 'struct' metatype qapi: Prefer 'struct' over 'type' in generator qapi: More rigorous checking for type safety bypass qapi: Whitelist commands that don't return dictionary qapi: Require valid names qapi: More rigourous checking of types qapi: Add some type check tests qapi: Unify type bypass and add tests qapi: Allow true, false and null in schema json ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--docs/qapi-code-gen.txt487
-rw-r--r--docs/qmp/qmp-spec.txt115
-rw-r--r--hmp.c28
-rw-r--r--hw/pci/pci.c42
-rw-r--r--qapi-schema.json264
-rw-r--r--qapi/block-core.json72
-rw-r--r--qapi/block.json2
-rw-r--r--qapi/common.json30
-rw-r--r--qapi/trace.json2
-rw-r--r--qga/qapi-schema.json36
-rw-r--r--qmp.c9
-rw-r--r--scripts/qapi-commands.py17
-rw-r--r--scripts/qapi-event.py4
-rw-r--r--scripts/qapi-types.py77
-rw-r--r--scripts/qapi-visit.py128
-rw-r--r--scripts/qapi.py530
-rw-r--r--tests/Makefile38
-rw-r--r--tests/qapi-schema/alternate-array.err1
-rw-r--r--tests/qapi-schema/alternate-array.exit1
-rw-r--r--tests/qapi-schema/alternate-array.json7
-rw-r--r--tests/qapi-schema/alternate-array.out0
-rw-r--r--tests/qapi-schema/alternate-base.err1
-rw-r--r--tests/qapi-schema/alternate-base.exit1
-rw-r--r--tests/qapi-schema/alternate-base.json6
-rw-r--r--tests/qapi-schema/alternate-base.out0
-rw-r--r--tests/qapi-schema/alternate-clash.err1
-rw-r--r--tests/qapi-schema/alternate-clash.exit1
-rw-r--r--tests/qapi-schema/alternate-clash.json3
-rw-r--r--tests/qapi-schema/alternate-clash.out0
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.err1
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.exit1
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.json8
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.out0
-rw-r--r--tests/qapi-schema/alternate-conflict-string.err1
-rw-r--r--tests/qapi-schema/alternate-conflict-string.exit1
-rw-r--r--tests/qapi-schema/alternate-conflict-string.json6
-rw-r--r--tests/qapi-schema/alternate-conflict-string.out0
-rw-r--r--tests/qapi-schema/alternate-good.err0
-rw-r--r--tests/qapi-schema/alternate-good.exit1
-rw-r--r--tests/qapi-schema/alternate-good.json9
-rw-r--r--tests/qapi-schema/alternate-good.out6
-rw-r--r--tests/qapi-schema/alternate-nested.err1
-rw-r--r--tests/qapi-schema/alternate-nested.exit1
-rw-r--r--tests/qapi-schema/alternate-nested.json5
-rw-r--r--tests/qapi-schema/alternate-nested.out0
-rw-r--r--tests/qapi-schema/alternate-unknown.err1
-rw-r--r--tests/qapi-schema/alternate-unknown.exit1
-rw-r--r--tests/qapi-schema/alternate-unknown.json3
-rw-r--r--tests/qapi-schema/alternate-unknown.out0
-rw-r--r--tests/qapi-schema/bad-base.err1
-rw-r--r--tests/qapi-schema/bad-base.exit1
-rw-r--r--tests/qapi-schema/bad-base.json3
-rw-r--r--tests/qapi-schema/bad-base.out0
-rw-r--r--tests/qapi-schema/bad-data.err1
-rw-r--r--tests/qapi-schema/bad-data.exit1
-rw-r--r--tests/qapi-schema/bad-data.json2
-rw-r--r--tests/qapi-schema/bad-data.out0
-rw-r--r--tests/qapi-schema/bad-ident.err1
-rw-r--r--tests/qapi-schema/bad-ident.exit1
-rw-r--r--tests/qapi-schema/bad-ident.json2
-rw-r--r--tests/qapi-schema/bad-ident.out0
-rw-r--r--tests/qapi-schema/bad-type-bool.err1
-rw-r--r--tests/qapi-schema/bad-type-bool.exit1
-rw-r--r--tests/qapi-schema/bad-type-bool.json2
-rw-r--r--tests/qapi-schema/bad-type-bool.out0
-rw-r--r--tests/qapi-schema/bad-type-dict.err1
-rw-r--r--tests/qapi-schema/bad-type-dict.exit1
-rw-r--r--tests/qapi-schema/bad-type-dict.json2
-rw-r--r--tests/qapi-schema/bad-type-dict.out0
-rw-r--r--tests/qapi-schema/bad-type-int.err1
-rw-r--r--tests/qapi-schema/bad-type-int.exit1
-rw-r--r--tests/qapi-schema/bad-type-int.json3
-rw-r--r--tests/qapi-schema/bad-type-int.out0
-rw-r--r--tests/qapi-schema/command-int.err1
-rw-r--r--tests/qapi-schema/command-int.exit1
-rw-r--r--tests/qapi-schema/command-int.json3
-rw-r--r--tests/qapi-schema/command-int.out0
-rw-r--r--tests/qapi-schema/data-array-empty.err1
-rw-r--r--tests/qapi-schema/data-array-empty.exit1
-rw-r--r--tests/qapi-schema/data-array-empty.json2
-rw-r--r--tests/qapi-schema/data-array-empty.out0
-rw-r--r--tests/qapi-schema/data-array-unknown.err1
-rw-r--r--tests/qapi-schema/data-array-unknown.exit1
-rw-r--r--tests/qapi-schema/data-array-unknown.json2
-rw-r--r--tests/qapi-schema/data-array-unknown.out0
-rw-r--r--tests/qapi-schema/data-int.err1
-rw-r--r--tests/qapi-schema/data-int.exit1
-rw-r--r--tests/qapi-schema/data-int.json2
-rw-r--r--tests/qapi-schema/data-int.out0
-rw-r--r--tests/qapi-schema/data-member-array-bad.err1
-rw-r--r--tests/qapi-schema/data-member-array-bad.exit1
-rw-r--r--tests/qapi-schema/data-member-array-bad.json2
-rw-r--r--tests/qapi-schema/data-member-array-bad.out0
-rw-r--r--tests/qapi-schema/data-member-array.err0
-rw-r--r--tests/qapi-schema/data-member-array.exit1
-rw-r--r--tests/qapi-schema/data-member-array.json4
-rw-r--r--tests/qapi-schema/data-member-array.out5
-rw-r--r--tests/qapi-schema/data-member-unknown.err1
-rw-r--r--tests/qapi-schema/data-member-unknown.exit1
-rw-r--r--tests/qapi-schema/data-member-unknown.json2
-rw-r--r--tests/qapi-schema/data-member-unknown.out0
-rw-r--r--tests/qapi-schema/data-unknown.err1
-rw-r--r--tests/qapi-schema/data-unknown.exit1
-rw-r--r--tests/qapi-schema/data-unknown.json2
-rw-r--r--tests/qapi-schema/data-unknown.out0
-rw-r--r--tests/qapi-schema/double-data.err1
-rw-r--r--tests/qapi-schema/double-data.exit1
-rw-r--r--tests/qapi-schema/double-data.json2
-rw-r--r--tests/qapi-schema/double-data.out0
-rw-r--r--tests/qapi-schema/double-type.err1
-rw-r--r--tests/qapi-schema/double-type.exit1
-rw-r--r--tests/qapi-schema/double-type.json2
-rw-r--r--tests/qapi-schema/double-type.out0
-rw-r--r--tests/qapi-schema/enum-bad-name.err1
-rw-r--r--tests/qapi-schema/enum-bad-name.exit1
-rw-r--r--tests/qapi-schema/enum-bad-name.json2
-rw-r--r--tests/qapi-schema/enum-bad-name.out0
-rw-r--r--tests/qapi-schema/enum-clash-member.err1
-rw-r--r--tests/qapi-schema/enum-clash-member.exit1
-rw-r--r--tests/qapi-schema/enum-clash-member.json2
-rw-r--r--tests/qapi-schema/enum-clash-member.out0
-rw-r--r--tests/qapi-schema/enum-dict-member.err1
-rw-r--r--tests/qapi-schema/enum-dict-member.exit1
-rw-r--r--tests/qapi-schema/enum-dict-member.json2
-rw-r--r--tests/qapi-schema/enum-dict-member.out0
-rw-r--r--tests/qapi-schema/enum-empty.err0
-rw-r--r--tests/qapi-schema/enum-empty.exit1
-rw-r--r--tests/qapi-schema/enum-empty.json2
-rw-r--r--tests/qapi-schema/enum-empty.out3
-rw-r--r--tests/qapi-schema/enum-int-member.err1
-rw-r--r--tests/qapi-schema/enum-int-member.exit1
-rw-r--r--tests/qapi-schema/enum-int-member.json3
-rw-r--r--tests/qapi-schema/enum-int-member.out0
-rw-r--r--tests/qapi-schema/enum-max-member.err1
-rw-r--r--tests/qapi-schema/enum-max-member.exit1
-rw-r--r--tests/qapi-schema/enum-max-member.json3
-rw-r--r--tests/qapi-schema/enum-max-member.out0
-rw-r--r--tests/qapi-schema/enum-missing-data.err1
-rw-r--r--tests/qapi-schema/enum-missing-data.exit1
-rw-r--r--tests/qapi-schema/enum-missing-data.json2
-rw-r--r--tests/qapi-schema/enum-missing-data.out0
-rw-r--r--tests/qapi-schema/enum-union-clash.err1
-rw-r--r--tests/qapi-schema/enum-union-clash.exit1
-rw-r--r--tests/qapi-schema/enum-union-clash.json4
-rw-r--r--tests/qapi-schema/enum-union-clash.out0
-rw-r--r--tests/qapi-schema/enum-wrong-data.err1
-rw-r--r--tests/qapi-schema/enum-wrong-data.exit1
-rw-r--r--tests/qapi-schema/enum-wrong-data.json2
-rw-r--r--tests/qapi-schema/enum-wrong-data.out0
-rw-r--r--tests/qapi-schema/escape-outside-string.err1
-rw-r--r--tests/qapi-schema/escape-outside-string.exit1
-rw-r--r--tests/qapi-schema/escape-outside-string.json3
-rw-r--r--tests/qapi-schema/escape-outside-string.out0
-rw-r--r--tests/qapi-schema/escape-too-big.err1
-rw-r--r--tests/qapi-schema/escape-too-big.exit1
-rw-r--r--tests/qapi-schema/escape-too-big.json3
-rw-r--r--tests/qapi-schema/escape-too-big.out0
-rw-r--r--tests/qapi-schema/escape-too-short.err1
-rw-r--r--tests/qapi-schema/escape-too-short.exit1
-rw-r--r--tests/qapi-schema/escape-too-short.json3
-rw-r--r--tests/qapi-schema/escape-too-short.out0
-rw-r--r--tests/qapi-schema/event-case.err0
-rw-r--r--tests/qapi-schema/event-case.exit1
-rw-r--r--tests/qapi-schema/event-case.json3
-rw-r--r--tests/qapi-schema/event-case.out3
-rw-r--r--tests/qapi-schema/event-max.err1
-rw-r--r--tests/qapi-schema/event-max.exit1
-rw-r--r--tests/qapi-schema/event-max.json2
-rw-r--r--tests/qapi-schema/event-max.out0
-rw-r--r--tests/qapi-schema/event-nest-struct.err2
-rw-r--r--tests/qapi-schema/flat-union-bad-base.err1
-rw-r--r--tests/qapi-schema/flat-union-bad-base.exit1
-rw-r--r--tests/qapi-schema/flat-union-bad-base.json13
-rw-r--r--tests/qapi-schema/flat-union-bad-base.out0
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.err1
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.exit1
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.json15
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.out0
-rw-r--r--tests/qapi-schema/flat-union-base-star.err1
-rw-r--r--tests/qapi-schema/flat-union-base-star.exit1
-rw-r--r--tests/qapi-schema/flat-union-base-star.json12
-rw-r--r--tests/qapi-schema/flat-union-base-star.out0
-rw-r--r--tests/qapi-schema/flat-union-base-union.err1
-rw-r--r--tests/qapi-schema/flat-union-base-union.exit1
-rw-r--r--tests/qapi-schema/flat-union-base-union.json15
-rw-r--r--tests/qapi-schema/flat-union-base-union.out0
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.err1
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.exit1
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.json14
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.out0
-rw-r--r--tests/qapi-schema/flat-union-inline.err1
-rw-r--r--tests/qapi-schema/flat-union-inline.exit1
-rw-r--r--tests/qapi-schema/flat-union-inline.json11
-rw-r--r--tests/qapi-schema/flat-union-inline.out0
-rw-r--r--tests/qapi-schema/flat-union-int-branch.err1
-rw-r--r--tests/qapi-schema/flat-union-int-branch.exit1
-rw-r--r--tests/qapi-schema/flat-union-int-branch.json12
-rw-r--r--tests/qapi-schema/flat-union-int-branch.out0
-rw-r--r--tests/qapi-schema/flat-union-invalid-branch-key.json6
-rw-r--r--tests/qapi-schema/flat-union-invalid-discriminator.err2
-rw-r--r--tests/qapi-schema/flat-union-invalid-discriminator.json6
-rw-r--r--tests/qapi-schema/flat-union-no-base.err2
-rw-r--r--tests/qapi-schema/flat-union-no-base.json12
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.err1
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.exit1
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.json10
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.out0
-rw-r--r--tests/qapi-schema/flat-union-reverse-define.json6
-rw-r--r--tests/qapi-schema/flat-union-reverse-define.out12
-rw-r--r--tests/qapi-schema/flat-union-string-discriminator.json6
-rw-r--r--tests/qapi-schema/ident-with-escape.err0
-rw-r--r--tests/qapi-schema/ident-with-escape.exit1
-rw-r--r--tests/qapi-schema/ident-with-escape.json4
-rw-r--r--tests/qapi-schema/ident-with-escape.out3
-rw-r--r--tests/qapi-schema/indented-expr.json4
-rw-r--r--tests/qapi-schema/indented-expr.out2
-rw-r--r--tests/qapi-schema/missing-type.err1
-rw-r--r--tests/qapi-schema/missing-type.exit1
-rw-r--r--tests/qapi-schema/missing-type.json2
-rw-r--r--tests/qapi-schema/missing-type.out0
-rw-r--r--tests/qapi-schema/nested-struct-data.err1
-rw-r--r--tests/qapi-schema/nested-struct-data.exit1
-rw-r--r--tests/qapi-schema/nested-struct-data.json4
-rw-r--r--tests/qapi-schema/nested-struct-data.out0
-rw-r--r--tests/qapi-schema/nested-struct-returns.err1
-rw-r--r--tests/qapi-schema/nested-struct-returns.exit1
-rw-r--r--tests/qapi-schema/nested-struct-returns.json3
-rw-r--r--tests/qapi-schema/nested-struct-returns.out0
-rw-r--r--tests/qapi-schema/qapi-schema-test.json46
-rw-r--r--tests/qapi-schema/qapi-schema-test.out54
-rw-r--r--tests/qapi-schema/redefined-builtin.err1
-rw-r--r--tests/qapi-schema/redefined-builtin.exit1
-rw-r--r--tests/qapi-schema/redefined-builtin.json2
-rw-r--r--tests/qapi-schema/redefined-builtin.out0
-rw-r--r--tests/qapi-schema/redefined-command.err1
-rw-r--r--tests/qapi-schema/redefined-command.exit1
-rw-r--r--tests/qapi-schema/redefined-command.json3
-rw-r--r--tests/qapi-schema/redefined-command.out0
-rw-r--r--tests/qapi-schema/redefined-event.err1
-rw-r--r--tests/qapi-schema/redefined-event.exit1
-rw-r--r--tests/qapi-schema/redefined-event.json3
-rw-r--r--tests/qapi-schema/redefined-event.out0
-rw-r--r--tests/qapi-schema/redefined-type.err1
-rw-r--r--tests/qapi-schema/redefined-type.exit1
-rw-r--r--tests/qapi-schema/redefined-type.json3
-rw-r--r--tests/qapi-schema/redefined-type.out0
-rw-r--r--tests/qapi-schema/returns-alternate.err1
-rw-r--r--tests/qapi-schema/returns-alternate.exit1
-rw-r--r--tests/qapi-schema/returns-alternate.json3
-rw-r--r--tests/qapi-schema/returns-alternate.out0
-rw-r--r--tests/qapi-schema/returns-array-bad.err1
-rw-r--r--tests/qapi-schema/returns-array-bad.exit1
-rw-r--r--tests/qapi-schema/returns-array-bad.json2
-rw-r--r--tests/qapi-schema/returns-array-bad.out0
-rw-r--r--tests/qapi-schema/returns-int.err0
-rw-r--r--tests/qapi-schema/returns-int.exit1
-rw-r--r--tests/qapi-schema/returns-int.json3
-rw-r--r--tests/qapi-schema/returns-int.out3
-rw-r--r--tests/qapi-schema/returns-unknown.err1
-rw-r--r--tests/qapi-schema/returns-unknown.exit1
-rw-r--r--tests/qapi-schema/returns-unknown.json2
-rw-r--r--tests/qapi-schema/returns-unknown.out0
-rw-r--r--tests/qapi-schema/returns-whitelist.err1
-rw-r--r--tests/qapi-schema/returns-whitelist.exit1
-rw-r--r--tests/qapi-schema/returns-whitelist.json11
-rw-r--r--tests/qapi-schema/returns-whitelist.out0
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.err1
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.exit1
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.json9
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.out0
-rw-r--r--tests/qapi-schema/struct-base-clash.err1
-rw-r--r--tests/qapi-schema/struct-base-clash.exit1
-rw-r--r--tests/qapi-schema/struct-base-clash.json6
-rw-r--r--tests/qapi-schema/struct-base-clash.out0
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.err1
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.exit1
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.json2
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.out0
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.err1
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.exit1
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.json2
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.out0
-rw-r--r--tests/qapi-schema/type-bypass.err0
-rw-r--r--tests/qapi-schema/type-bypass.exit1
-rw-r--r--tests/qapi-schema/type-bypass.json2
-rw-r--r--tests/qapi-schema/type-bypass.out3
-rw-r--r--tests/qapi-schema/unicode-str.err1
-rw-r--r--tests/qapi-schema/unicode-str.exit1
-rw-r--r--tests/qapi-schema/unicode-str.json2
-rw-r--r--tests/qapi-schema/unicode-str.out0
-rw-r--r--tests/qapi-schema/union-bad-branch.err1
-rw-r--r--tests/qapi-schema/union-bad-branch.exit1
-rw-r--r--tests/qapi-schema/union-bad-branch.json8
-rw-r--r--tests/qapi-schema/union-bad-branch.out0
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.err1
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.exit1
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.json14
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.out0
-rw-r--r--tests/qapi-schema/union-invalid-base.err2
-rw-r--r--tests/qapi-schema/union-invalid-base.json8
-rw-r--r--tests/qapi-schema/union-max.err1
-rw-r--r--tests/qapi-schema/union-max.exit1
-rw-r--r--tests/qapi-schema/union-max.json3
-rw-r--r--tests/qapi-schema/union-max.out0
-rw-r--r--tests/qapi-schema/union-optional-branch.err1
-rw-r--r--tests/qapi-schema/union-optional-branch.exit1
-rw-r--r--tests/qapi-schema/union-optional-branch.json2
-rw-r--r--tests/qapi-schema/union-optional-branch.out0
-rw-r--r--tests/qapi-schema/union-unknown.err1
-rw-r--r--tests/qapi-schema/union-unknown.exit1
-rw-r--r--tests/qapi-schema/union-unknown.json3
-rw-r--r--tests/qapi-schema/union-unknown.out0
-rw-r--r--tests/qapi-schema/unknown-escape.err1
-rw-r--r--tests/qapi-schema/unknown-escape.exit1
-rw-r--r--tests/qapi-schema/unknown-escape.json3
-rw-r--r--tests/qapi-schema/unknown-escape.out0
-rw-r--r--tests/qapi-schema/unknown-expr-key.err1
-rw-r--r--tests/qapi-schema/unknown-expr-key.exit1
-rw-r--r--tests/qapi-schema/unknown-expr-key.json2
-rw-r--r--tests/qapi-schema/unknown-expr-key.out0
-rw-r--r--tests/test-qmp-commands.c37
-rw-r--r--tests/test-qmp-input-strict.c98
-rw-r--r--tests/test-qmp-input-visitor.c119
-rw-r--r--tests/test-qmp-output-visitor.c156
-rw-r--r--tests/test-visitor-serialization.c87
325 files changed, 2109 insertions, 927 deletions
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 8313ba6af8..269a1f3d27 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -1,61 +1,193 @@
= How to use the QAPI code generator =
-QAPI is a native C API within QEMU which provides management-level
-functionality to internal/external users. For external
-users/processes, this interface is made available by a JSON-based
-QEMU Monitor protocol that is provided by the QMP server.
-
-To map QMP-defined interfaces to the native C QAPI implementations,
-a JSON-based schema is used to define types and function
-signatures, and a set of scripts is used to generate types/signatures,
-and marshaling/dispatch code. The QEMU Guest Agent also uses these
-scripts, paired with a separate schema, to generate
-marshaling/dispatch code for the guest agent server running in the
-guest.
+Copyright IBM Corp. 2011
+Copyright (C) 2012-2015 Red Hat, Inc.
-This document will describe how the schemas, scripts, and resulting
-code are used.
+This work is licensed under the terms of the GNU GPL, version 2 or
+later. See the COPYING file in the top-level directory.
+== Introduction ==
-== QMP/Guest agent schema ==
-
-This file defines the types, commands, and events used by QMP. It should
-fully describe the interface used by QMP.
+QAPI is a native C API within QEMU which provides management-level
+functionality to internal and external users. For external
+users/processes, this interface is made available by a JSON-based wire
+format for the QEMU Monitor Protocol (QMP) for controlling qemu, as
+well as the QEMU Guest Agent (QGA) for communicating with the guest.
+The remainder of this document uses "Client JSON Protocol" when
+referring to the wire contents of a QMP or QGA connection.
-This file is designed to be loosely based on JSON although it's technically
-executable Python. While dictionaries are used, they are parsed as
-OrderedDicts so that ordering is preserved.
+To map Client JSON Protocol interfaces to the native C QAPI
+implementations, a JSON-based schema is used to define types and
+function signatures, and a set of scripts is used to generate types,
+signatures, and marshaling/dispatch code. This document will describe
+how the schemas, scripts, and resulting code are used.
-There are two basic syntaxes used, type definitions and command definitions.
-The first syntax defines a type and is represented by a dictionary. There are
-three kinds of user-defined types that are supported: complex types,
-enumeration types and union types.
+== QMP/Guest agent schema ==
-Generally speaking, types definitions should always use CamelCase for the type
-names. Command names should be all lower case with words separated by a hyphen.
+A QAPI schema file is designed to be loosely based on JSON
+(http://www.ietf.org/rfc/rfc7159.txt) with changes for quoting style
+and the use of comments; a QAPI schema file is then parsed by a python
+code generation program. A valid QAPI schema consists of a series of
+top-level expressions, with no commas between them. Where
+dictionaries (JSON objects) are used, they are parsed as python
+OrderedDicts so that ordering is preserved (for predictable layout of
+generated C structs and parameter lists). Ordering doesn't matter
+between top-level expressions or the keys within an expression, but
+does matter within dictionary values for 'data' and 'returns' members
+of a single expression. QAPI schema input is written using 'single
+quotes' instead of JSON's "double quotes" (in contrast, Client JSON
+Protocol uses no comments, and while input accepts 'single quotes' as
+an extension, output is strict JSON using only "double quotes"). As
+in JSON, trailing commas are not permitted in arrays or dictionaries.
+Input must be ASCII (although QMP supports full Unicode strings, the
+QAPI parser does not). At present, there is no place where a QAPI
+schema requires the use of JSON numbers or null.
+
+Comments are allowed; anything between an unquoted # and the following
+newline is ignored. Although there is not yet a documentation
+generator, a form of stylized comments has developed for consistently
+documenting details about an expression and when it was added to the
+schema. The documentation is delimited between two lines of ##, then
+the first line names the expression, an optional overview is provided,
+then individual documentation about each member of 'data' is provided,
+and finally, a 'Since: x.y.z' tag lists the release that introduced
+the expression. Optional fields are tagged with the phrase
+'#optional', often with their default value; and extensions added
+after the expression was first released are also given a '(since
+x.y.z)' comment. For example:
+
+ ##
+ # @BlockStats:
+ #
+ # Statistics of a virtual block device or a block backing device.
+ #
+ # @device: #optional If the stats are for a virtual block device, the name
+ # corresponding to the virtual block device.
+ #
+ # @stats: A @BlockDeviceStats for the device.
+ #
+ # @parent: #optional This describes the file block device if it has one.
+ #
+ # @backing: #optional This describes the backing block device if it has one.
+ # (Since 2.0)
+ #
+ # Since: 0.14.0
+ ##
+ { 'struct': 'BlockStats',
+ 'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
+ '*parent': 'BlockStats',
+ '*backing': 'BlockStats'} }
+
+The schema sets up a series of types, as well as commands and events
+that will use those types. Forward references are allowed: the parser
+scans in two passes, where the first pass learns all type names, and
+the second validates the schema and generates the code. This allows
+the definition of complex structs that can have mutually recursive
+types, and allows for indefinite nesting of Client JSON Protocol that
+satisfies the schema. A type name should not be defined more than
+once. It is permissible for the schema to contain additional types
+not used by any commands or events in the Client JSON Protocol, for
+the side effect of generated C code used internally.
+
+There are seven top-level expressions recognized by the parser:
+'include', 'command', 'struct', 'enum', 'union', 'alternate', and
+'event'. There are several groups of types: simple types (a number of
+built-in types, such as 'int' and 'str'; as well as enumerations),
+complex types (structs and two flavors of unions), and alternate types
+(a choice between other types). The 'command' and 'event' expressions
+can refer to existing types by name, or list an anonymous type as a
+dictionary. Listing a type name inside an array refers to a
+single-dimension array of that type; multi-dimension arrays are not
+directly supported (although an array of a complex struct that
+contains an array member is possible).
+
+Types, commands, and events share a common namespace. Therefore,
+generally speaking, type definitions should always use CamelCase for
+user-defined type names, while built-in types are lowercase. Type
+definitions should not end in 'Kind', as this namespace is used for
+creating implicit C enums for visiting union types. Command names,
+and field names within a type, should be all lower case with words
+separated by a hyphen. However, some existing older commands and
+complex types use underscore; when extending such expressions,
+consistency is preferred over blindly avoiding underscore. Event
+names should be ALL_CAPS with words separated by underscore. The
+special string '**' appears for some commands that manually perform
+their own type checking rather than relying on the type-safe code
+produced by the qapi code generators.
+
+Any name (command, event, type, field, or enum value) beginning with
+"x-" is marked experimental, and may be withdrawn or changed
+incompatibly in a future release. Downstream vendors may add
+extensions; such extensions should begin with a prefix matching
+"__RFQDN_" (for the reverse-fully-qualified-domain-name of the
+vendor), even if the rest of the name uses dash (example:
+__com.redhat_drive-mirror). Other than downstream extensions (with
+leading underscore and the use of dots), all names should begin with a
+letter, and contain only ASCII letters, digits, dash, and underscore.
+It is okay to reuse names that match C keywords; the generator will
+rename a field named "default" in the QAPI to "q_default" in the
+generated C code.
+
+In the rest of this document, usage lines are given for each
+expression type, with literal strings written in lower case and
+placeholders written in capitals. If a literal string includes a
+prefix of '*', that key/value pair can be omitted from the expression.
+For example, a usage statement that includes '*base':STRUCT-NAME
+means that an expression has an optional key 'base', which if present
+must have a value that forms a struct name.
+
+
+=== Built-in Types ===
+
+The following types are built-in to the parser:
+ 'str' - arbitrary UTF-8 string
+ 'int' - 64-bit signed integer (although the C code may place further
+ restrictions on acceptable range)
+ 'number' - floating point number
+ 'bool' - JSON value of true or false
+ 'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum
+ bit size
+ 'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts
+ 'size' - like 'uint64', but allows scaled suffix from command line
+ visitor
=== Includes ===
+Usage: { 'include': STRING }
+
The QAPI schema definitions can be modularized using the 'include' directive:
- { 'include': 'path/to/file.json'}
+ { 'include': 'path/to/file.json' }
The directive is evaluated recursively, and include paths are relative to the
-file using the directive. Multiple includes of the same file are safe.
+file using the directive. Multiple includes of the same file are
+safe. No other keys should appear in the expression, and the include
+value should be a string.
+
+As a matter of style, it is a good idea to have all files be
+self-contained, but at the moment, nothing prevents an included file
+from making a forward reference to a type that is only introduced by
+an outer file. The parser may be made stricter in the future to
+prevent incomplete include files.
-=== Complex types ===
+=== Struct types ===
-A complex type is a dictionary containing a single key whose value is a
-dictionary. This corresponds to a struct in C or an Object in JSON. An
-example of a complex type is:
+Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME }
- { 'type': 'MyType',
+A struct is a dictionary containing a single 'data' key whose
+value is a dictionary. This corresponds to a struct in C or an Object
+in JSON. Each value of the 'data' dictionary must be the name of a
+type, or a one-element array containing a type name. An example of a
+struct is:
+
+ { 'struct': 'MyType',
'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } }
-The use of '*' as a prefix to the name means the member is optional.
+The use of '*' as a prefix to the name means the member is optional in
+the corresponding JSON protocol usage.
The default initialization value of an optional argument should not be changed
between versions of QEMU unless the new default maintains backward
@@ -84,13 +216,13 @@ A structure that is used in both input and output of various commands
must consider the backwards compatibility constraints of both directions
of use.
-A complex type definition can specify another complex type as its base.
+A struct definition can specify another struct as its base.
In this case, the fields of the base type are included as top-level fields
-of the new complex type's dictionary in the QMP wire format. An example
-definition is:
+of the new struct's dictionary in the Client JSON Protocol wire
+format. An example definition is:
- { 'type': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'str' } }
- { 'type': 'BlockdevOptionsGenericCOWFormat',
+ { 'struct': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'str' } }
+ { 'struct': 'BlockdevOptionsGenericCOWFormat',
'base': 'BlockdevOptionsGenericFormat',
'data': { '*backing': 'str' } }
@@ -100,97 +232,158 @@ both fields like this:
{ "file": "/some/place/my-image",
"backing": "/some/place/my-backing-file" }
+
=== Enumeration types ===
-An enumeration type is a dictionary containing a single key whose value is a
-list of strings. An example enumeration is:
+Usage: { 'enum': STRING, 'data': ARRAY-OF-STRING }
+
+An enumeration type is a dictionary containing a single 'data' key
+whose value is a list of strings. An example enumeration is:
{ 'enum': 'MyEnum', 'data': [ 'value1', 'value2', 'value3' ] }
+Nothing prevents an empty enumeration, although it is probably not
+useful. The list of strings should be lower case; if an enum name
+represents multiple words, use '-' between words. The string 'max' is
+not allowed as an enum value, and values should not be repeated.
+
+The enumeration values are passed as strings over the Client JSON
+Protocol, but are encoded as C enum integral values in generated code.
+While the C code starts numbering at 0, it is better to use explicit
+comparisons to enum values than implicit comparisons to 0; the C code
+will also include a generated enum member ending in _MAX for tracking
+the size of the enum, useful when using common functions for
+converting between strings and enum values. Since the wire format
+always passes by name, it is acceptable to reorder or add new
+enumeration members in any location without breaking clients of Client
+JSON Protocol; however, removing enum values would break
+compatibility. For any struct that has a field that will only contain
+a finite set of string values, using an enum type for that field is
+better than open-coding the field to be type 'str'.
+
+
=== Union types ===
-Union types are used to let the user choose between several different data
-types. A union type is defined using a dictionary as explained in the
-following paragraphs.
+Usage: { 'union': STRING, 'data': DICT }
+or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME,
+ 'discriminator': ENUM-MEMBER-OF-BASE }
+Union types are used to let the user choose between several different
+variants for an object. There are two flavors: simple (no
+discriminator or base), flat (both discriminator and base). A union
+type is defined using a data dictionary as explained in the following
+paragraphs.
-A simple union type defines a mapping from discriminator values to data types
-like in this example:
+A simple union type defines a mapping from automatic discriminator
+values to data types like in this example:
- { 'type': 'FileOptions', 'data': { 'filename': 'str' } }
- { 'type': 'Qcow2Options',
+ { 'struct': 'FileOptions', 'data': { 'filename': 'str' } }
+ { 'struct': 'Qcow2Options',
'data': { 'backing-file': 'str', 'lazy-refcounts': 'bool' } }
{ 'union': 'BlockdevOptions',
'data': { 'file': 'FileOptions',
'qcow2': 'Qcow2Options' } }
-In the QMP wire format, a simple union is represented by a dictionary that
-contains the 'type' field as a discriminator, and a 'data' field that is of the
-specified data type corresponding to the discriminator value:
+In the Client JSON Protocol, a simple union is represented by a
+dictionary that contains the 'type' field as a discriminator, and a
+'data' field that is of the specified data type corresponding to the
+discriminator value, as in these examples:
+ { "type": "file", "data" : { "filename": "/some/place/my-image" } }
{ "type": "qcow2", "data" : { "backing-file": "/some/place/my-image",
"lazy-refcounts": true } }
+The generated C code uses a struct containing a union. Additionally,
+an implicit C enum 'NameKind' is created, corresponding to the union
+'Name', for accessing the various branches of the union. No branch of
+the union can be named 'max', as this would collide with the implicit
+enum. The value for each branch can be of any type.
-A union definition can specify a complex type as its base. In this case, the
-fields of the complex type are included as top-level fields of the union
-dictionary in the QMP wire format. An example definition is:
- { 'type': 'BlockdevCommonOptions', 'data': { 'readonly': 'bool' } }
- { 'union': 'BlockdevOptions',
- 'base': 'BlockdevCommonOptions',
- 'data': { 'raw': 'RawOptions',
- 'qcow2': 'Qcow2Options' } }
+A flat union definition specifies a struct as its base, and
+avoids nesting on the wire. All branches of the union must be
+complex types, and the top-level fields of the union dictionary on
+the wire will be combination of fields from both the base type and the
+appropriate branch type (when merging two dictionaries, there must be
+no keys in common). The 'discriminator' field must be the name of an
+enum-typed member of the base struct.
-And it looks like this on the wire:
-
- { "type": "qcow2",
- "readonly": false,
- "data" : { "backing-file": "/some/place/my-image",
- "lazy-refcounts": true } }
-
-
-Flat union types avoid the nesting on the wire. They are used whenever a
-specific field of the base type is declared as the discriminator ('type' is
-then no longer generated). The discriminator must be of enumeration type.
-The above example can then be modified as follows:
+The following example enhances the above simple union example by
+adding a common field 'readonly', renaming the discriminator to
+something more applicable, and reducing the number of {} required on
+the wire:
{ 'enum': 'BlockdevDriver', 'data': [ 'raw', 'qcow2' ] }
- { 'type': 'BlockdevCommonOptions',
+ { 'struct': 'BlockdevCommonOptions',
'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } }
{ 'union': 'BlockdevOptions',
'base': 'BlockdevCommonOptions',
'discriminator': 'driver',
- 'data': { 'raw': 'RawOptions',
+ 'data': { 'file': 'FileOptions',
'qcow2': 'Qcow2Options' } }
-Resulting in this JSON object:
+Resulting in these JSON objects:
+
+ { "driver": "file", "readonly": true,
+ "filename": "/some/place/my-image" }
+ { "driver": "qcow2", "readonly": false,
+ "backing-file": "/some/place/my-image", "lazy-refcounts": true }
+
+Notice that in a flat union, the discriminator name is controlled by
+the user, but because it must map to a base member with enum type, the
+code generator can ensure that branches exist for all values of the
+enum (although the order of the keys need not match the declaration of
+the enum). In the resulting generated C data types, a flat union is
+represented as a struct with the base member fields included directly,
+and then a union of structures for each branch of the struct.
+
+A simple union can always be re-written as a flat union where the base
+class has a single member named 'type', and where each branch of the
+union has a struct with a single member named 'data'. That is,
- { "driver": "qcow2",
- "readonly": false,
- "backing-file": "/some/place/my-image",
- "lazy-refcounts": true }
+ { 'union': 'Simple', 'data': { 'one': 'str', 'two': 'int' } }
+is identical on the wire to:
-A special type of unions are anonymous unions. They don't form a dictionary in
-the wire format but allow the direct use of different types in their place. As
-they aren't structured, they don't have any explicit discriminator but use
-the (QObject) data type of their value as an implicit discriminator. This means
-that they are restricted to using only one discriminator value per QObject
-type. For example, you cannot have two different complex types in an anonymous
-union, or two different integer types.
+ { 'enum': 'Enum', 'data': ['one', 'two'] }
+ { 'struct': 'Base', 'data': { 'type': 'Enum' } }
+ { 'struct': 'Branch1', 'data': { 'data': 'str' } }
+ { 'struct': 'Branch2', 'data': { 'data': 'int' } }
+ { 'union': 'Flat': 'base': 'Base', 'discriminator': 'type',
+ 'data': { 'one': 'Branch1', 'two': 'Branch2' } }
-Anonymous unions are declared using an empty dictionary as their discriminator.
-The discriminator values never appear on the wire, they are only used in the
-generated C code. Anonymous unions cannot have a base type.
- { 'union': 'BlockRef',
- 'discriminator': {},
+=== Alternate types ===
+
+Usage: { 'alternate': STRING, 'data': DICT }
+
+An alternate type is one that allows a choice between two or more JSON
+data types (string, integer, number, or object, but currently not
+array) on the wire. The definition is similar to a simple union type,
+where each branch of the union names a QAPI type. For example:
+
+ { 'alternate': 'BlockRef',
'data': { 'definition': 'BlockdevOptions',
'reference': 'str' } }
-This example allows using both of the following example objects:
+Just like for a simple union, an implicit C enum 'NameKind' is created
+to enumerate the branches for the alternate 'Name'.
+
+Unlike a union, the discriminator string is never passed on the wire
+for the Client JSON Protocol. Instead, the value's JSON type serves
+as an implicit discriminator, which in turn means that an alternate
+can only express a choice between types represented differently in
+JSON. If a branch is typed as the 'bool' built-in, the alternate
+accepts true and false; if it is typed as any of the various numeric
+built-ins, it accepts a JSON number; if it is typed as a 'str'
+built-in or named enum type, it accepts a JSON string; and if it is
+typed as a complex type (struct or union), it accepts a JSON object.
+Two different complex types, for instance, aren't permitted, because
+both are represented as a JSON object.
+
+The example alternate declaration above allows using both of the
+following example objects:
{ "file": "my_existing_block_device_id" }
{ "file": { "driver": "file",
@@ -200,23 +393,95 @@ This example allows using both of the following example objects:
=== Commands ===
-Commands are defined by using a list containing three members. The first
-member is the command name, the second member is a dictionary containing
-arguments, and the third member is the return type.
-
-An example command is:
+Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
+ '*returns': TYPE-NAME-OR-DICT,
+ '*gen': false, '*success-response': false }
+
+Commands are defined by using a dictionary containing several members,
+where three members are most common. The 'command' member is a
+mandatory string, and determines the "execute" value passed in a
+Client JSON Protocol command exchange.
+
+The 'data' argument maps to the "arguments" dictionary passed in as
+part of a Client JSON Protocol command. The 'data' member is optional
+and defaults to {} (an empty dictionary). If present, it must be the
+string name of a complex type, a one-element array containing the name
+of a complex type, or a dictionary that declares an anonymous type
+with the same semantics as a 'struct' expression, with one exception
+noted below when 'gen' is used.
+
+The 'returns' member describes what will appear in the "return" field
+of a Client JSON Protocol reply on successful completion of a command.
+The member is optional from the command declaration; if absent, the
+"return" field will be an empty dictionary. If 'returns' is present,
+it must be the string name of a complex or built-in type, a
+one-element array containing the name of a complex or built-in type,
+or a dictionary that declares an anonymous type with the same
+semantics as a 'struct' expression, with one exception noted below
+when 'gen' is used. Although it is permitted to have the 'returns'
+member name a built-in type or an array of built-in types, any command
+that does this cannot be extended to return additional information in
+the future; thus, new commands should strongly consider returning a
+dictionary-based type or an array of dictionaries, even if the
+dictionary only contains one field at the present.
+
+All commands in Client JSON Protocol use a dictionary to report
+failure, with no way to specify that in QAPI. Where the error return
+is different than the usual GenericError class in order to help the
+client react differently to certain error conditions, it is worth
+documenting this in the comments before the command declaration.
+
+Some example commands:
+
+ { 'command': 'my-first-command',
+ 'data': { 'arg1': 'str', '*arg2': 'str' } }
+ { 'struct': 'MyType', 'data': { '*value': 'str' } }
+ { 'command': 'my-second-command',
+ 'returns': [ 'MyType' ] }
+
+which would validate this Client JSON Protocol transaction:
+
+ => { "execute": "my-first-command",
+ "arguments": { "arg1": "hello" } }
+ <= { "return": { } }
+ => { "execute": "my-second-command" }
+ <= { "return": [ { "value": "one" }, { } ] }
+
+In rare cases, QAPI cannot express a type-safe representation of a
+corresponding Client JSON Protocol command. In these cases, if the
+command expression includes the key 'gen' with boolean value false,
+then the 'data' or 'returns' member that intends to bypass generated
+type-safety and do its own manual validation should use an inline
+dictionary definition, with a value of '**' rather than a valid type
+name for the keys that the generated code will not validate. Please
+try to avoid adding new commands that rely on this, and instead use
+type-safe unions. For an example of bypass usage:
+
+ { 'command': 'netdev_add',
+ 'data': {'type': 'str', 'id': 'str', '*props': '**'},
+ 'gen': false }
+
+Normally, the QAPI schema is used to describe synchronous exchanges,
+where a response is expected. But in some cases, the action of a
+command is expected to change state in a way that a successful
+response is not possible (although the command will still return a
+normal dictionary error on failure). When a successful reply is not
+possible, the command expression should include the optional key
+'success-response' with boolean value false. So far, only QGA makes
+use of this field.
- { 'command': 'my-command',
- 'data': { 'arg1': 'str', '*arg2': 'str' },
- 'returns': 'str' }
=== Events ===
-Events are defined with the keyword 'event'. When 'data' is also specified,
-additional info will be included in the event. Finally there will be C API
-generated in qapi-event.h; when called by QEMU code, a message with timestamp
-will be emitted on the wire. If timestamp is -1, it means failure to retrieve
-host time.
+Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT }
+
+Events are defined with the keyword 'event'. It is not allowed to
+name an event 'MAX', since the generator also produces a C enumeration
+of all event names with a generated _MAX value at the end. When
+'data' is also specified, additional info will be included in the
+event, with similar semantics to a 'struct' expression. Finally there
+will be C API generated in qapi-event.h; when called by QEMU code, a
+message with timestamp will be emitted on the wire.
An example event is:
@@ -234,9 +499,9 @@ Resulting in this JSON object:
Schemas are fed into 3 scripts to generate all the code/files that, paired
with the core QAPI libraries, comprise everything required to take JSON
-commands read in by a QMP/guest agent server, unmarshal the arguments into
+commands read in by a Client JSON Protocol server, unmarshal the arguments into
the underlying C types, call into the corresponding C function, and map the
-response back to a QMP/guest agent response to be returned to the user.
+response back to a Client JSON Protocol response to be returned to the user.
As an example, we'll use the following schema, which describes a single
complex user-defined type (which will produce a C struct, along with a list
@@ -245,7 +510,7 @@ case we want to accept/return a list of this type with a command), and a
command which takes that type as a parameter and returns the same type:
$ cat example-schema.json
- { 'type': 'UserDefOne',
+ { 'struct': 'UserDefOne',
'data': { 'integer': 'int', 'string': 'str' } }
{ 'command': 'my-command',
@@ -311,7 +576,7 @@ Example:
#ifndef EXAMPLE_QAPI_TYPES_H
#define EXAMPLE_QAPI_TYPES_H
-[Builtin types omitted...]
+[Built-in types omitted...]
typedef struct UserDefOne UserDefOne;
@@ -324,7 +589,7 @@ Example:
struct UserDefOneList *next;
} UserDefOneList;
-[Functions on builtin types omitted...]
+[Functions on built-in types omitted...]
struct UserDefOne
{
@@ -423,7 +688,7 @@ Example:
#ifndef EXAMPLE_QAPI_VISIT_H
#define EXAMPLE_QAPI_VISIT_H
-[Visitors for builtin types omitted...]
+[Visitors for built-in types omitted...]
void visit_type_UserDefOne(Visitor *m, UserDefOne **obj, const char *name, Error **errp);
void visit_type_UserDefOneList(Visitor *m, UserDefOneList **obj, const char *name, Error **errp);
diff --git a/docs/qmp/qmp-spec.txt b/docs/qmp/qmp-spec.txt
index 22568c644e..4c28cd9438 100644
--- a/docs/qmp/qmp-spec.txt
+++ b/docs/qmp/qmp-spec.txt
@@ -1,10 +1,21 @@
QEMU Machine Protocol Specification
+0. About This Document
+======================
+
+Copyright (C) 2009-2015 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.
+
1. Introduction
===============
-This document specifies the QEMU Machine Protocol (QMP), a JSON-based protocol
-which is available for applications to operate QEMU at the machine-level.
+This document specifies the QEMU Machine Protocol (QMP), a JSON-based
+protocol which is available for applications to operate QEMU at the
+machine-level. It is also in use by the QEMU Guest Agent (QGA), which
+is available for host applications to interact with the guest
+operating system.
2. Protocol Specification
=========================
@@ -18,14 +29,27 @@ following format:
json-DATA-STRUCTURE-NAME
-Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by
-the JSON standard:
+Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined
+by the JSON standard:
+
+http://www.ietf.org/rfc/rfc7159.txt
-http://www.ietf.org/rfc/rfc4627.txt
+The protocol is always encoded in UTF-8 except for synchronization
+bytes (documented below); although thanks to json-string escape
+sequences, the server will reply using only the strict ASCII subset.
-For convenience, json-object members and json-array elements mentioned in
-this document will be in a certain order. However, in real protocol usage
-they can be in ANY order, thus no particular order should be assumed.
+For convenience, json-object members mentioned in this document will
+be in a certain order. However, in real protocol usage they can be in
+ANY order, thus no particular order should be assumed. On the other
+hand, use of json-array elements presumes that preserving order is
+important unless specifically documented otherwise. Repeating a key
+within a json-object gives unpredictable results.
+
+Also for convenience, the server will accept an extension of
+'single-quoted' strings in place of the usual "double-quoted"
+json-string, and both input forms of strings understand an additional
+escape sequence of "\'" for a single quote. The server will only use
+double quoting on output.
2.1 General Definitions
-----------------------
@@ -52,7 +76,16 @@ The greeting message format is:
- The "version" member contains the Server's version information (the format
is the same of the query-version command)
- The "capabilities" member specify the availability of features beyond the
- baseline specification
+ baseline specification; the order of elements in this array has no
+ particular significance, so a client must search the entire array
+ when looking for a particular capability
+
+2.2.1 Capabilities
+------------------
+
+As of the date this document was last revised, no server or client
+capability strings have been defined.
+
2.3 Issuing Commands
--------------------
@@ -65,10 +98,14 @@ The format for command execution is:
- The "execute" member identifies the command to be executed by the Server
- The "arguments" member is used to pass any arguments required for the
- execution of the command, it is optional when no arguments are required
+ execution of the command, it is optional when no arguments are
+ required. Each command documents what contents will be considered
+ valid when handling the json-argument
- The "id" member is a transaction identification associated with the
command execution, it is optional and will be part of the response if
- provided
+ provided. The "id" member can be any json-value, although most
+ clients merely use a json-number incremented for each successive
+ command
2.4 Commands Responses
----------------------
@@ -81,13 +118,15 @@ of a command execution: success or error.
The format of a success response is:
-{ "return": json-object, "id": json-value }
+{ "return": json-value, "id": json-value }
Where,
-- The "return" member contains the command returned data, which is defined
- in a per-command basis or an empty json-object if the command does not
- return data
+- The "return" member contains the data returned by the command, which
+ is defined on a per-command basis (usually a json-object or
+ json-array of json-objects, but sometimes a json-number, json-string,
+ or json-array of json-strings); it is an empty json-object if the
+ command does not return data
- The "id" member contains the transaction identification associated
with the command execution if issued by the Client
@@ -114,7 +153,8 @@ if provided by the client.
-----------------------
As a result of state changes, the Server may send messages unilaterally
-to the Client at any time. They are called "asynchronous events".
+to the Client at any time, when not in the middle of any other
+response. They are called "asynchronous events".
The format of asynchronous events is:
@@ -126,13 +166,27 @@ The format of asynchronous events is:
- The "event" member contains the event's name
- The "data" member contains event specific data, which is defined in a
per-event basis, it is optional
-- The "timestamp" member contains the exact time of when the event occurred
- in the Server. It is a fixed json-object with time in seconds and
- microseconds
+- The "timestamp" member contains the exact time of when the event
+ occurred in the Server. It is a fixed json-object with time in
+ seconds and microseconds relative to the Unix Epoch (1 Jan 1970); if
+ there is a failure to retrieve host time, both members of the
+ timestamp will be set to -1.
For a listing of supported asynchronous events, please, refer to the
qmp-events.txt file.
+2.5 QGA Synchronization
+-----------------------
+
+When using QGA, an additional synchronization feature is built into
+the protocol. If the Client sends a raw 0xFF sentinel byte (not valid
+JSON), then the Server will reset its state and discard all pending
+data prior to the sentinel. Conversely, if the Client makes use of
+the 'guest-sync-delimited' command, the Server will send a raw 0xFF
+sentinel byte prior to its response, to aid the Client in discarding
+any data prior to the sentinel.
+
+
3. QMP Examples
===============
@@ -145,32 +199,37 @@ This section provides some examples of real QMP usage, in all of them
S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 },
"package": ""}, "capabilities": []}}
-3.2 Simple 'stop' execution
+3.2 Client QMP negotiation
+--------------------------
+C: { "execute": "qmp_capabilities" }
+S: { "return": {}}
+
+3.3 Simple 'stop' execution
---------------------------
C: { "execute": "stop" }
S: { "return": {} }
-3.3 KVM information
+3.4 KVM information
-------------------
C: { "execute": "query-kvm", "id": "example" }
S: { "return": { "enabled": true, "present": true }, "id": "example"}
-3.4 Parsing error
+3.5 Parsing error
------------------
C: { "execute": }
S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } }
-3.5 Powerdown event
+3.6 Powerdown event
-------------------
S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 },
"event": "POWERDOWN" }
4. Capabilities Negotiation
-----------------------------
+===========================
When a Client successfully establishes a connection, the Server is in
Capabilities Negotiation mode.
@@ -189,7 +248,7 @@ effect, all commands (except qmp_capabilities) are allowed and asynchronous
messages are delivered.
5 Compatibility Considerations
-------------------------------
+==============================
All protocol changes or new features which modify the protocol format in an
incompatible way are disabled by default and will be advertised by the
@@ -213,12 +272,16 @@ However, Clients must not assume any particular:
- Amount of errors generated by a command, that is, new errors can be added
to any existing command in newer versions of the Server
+Any command or field name beginning with "x-" is deemed experimental,
+and may be withdrawn or changed in an incompatible manner in a future
+release.
+
Of course, the Server does guarantee to send valid JSON. But apart from
this, a Client should be "conservative in what they send, and liberal in
what they accept".
6. Downstream extension of QMP
-------------------------------
+==============================
We recommend that downstream consumers of QEMU do *not* modify QMP.
Management tools should be able to support both upstream and downstream
diff --git a/hmp.c b/hmp.c
index d85d913a79..3010d04c92 100644
--- a/hmp.c
+++ b/hmp.c
@@ -60,7 +60,7 @@ void hmp_info_version(Monitor *mon, const QDict *qdict)
info = qmp_query_version(NULL);
monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n",
- info->qemu.major, info->qemu.minor, info->qemu.micro,
+ info->qemu->major, info->qemu->minor, info->qemu->micro,
info->package);
qapi_free_VersionInfo(info);
@@ -648,14 +648,14 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
dev->slot, dev->function);
monitor_printf(mon, " ");
- if (dev->class_info.has_desc) {
- monitor_printf(mon, "%s", dev->class_info.desc);
+ if (dev->class_info->has_desc) {
+ monitor_printf(mon, "%s", dev->class_info->desc);
} else {
- monitor_printf(mon, "Class %04" PRId64, dev->class_info.q_class);
+ monitor_printf(mon, "Class %04" PRId64, dev->class_info->q_class);
}
monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
- dev->id.vendor, dev->id.device);
+ dev->id->vendor, dev->id->device);
if (dev->has_irq) {
monitor_printf(mon, " IRQ %" PRId64 ".\n", dev->irq);
@@ -663,25 +663,25 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
if (dev->has_pci_bridge) {
monitor_printf(mon, " BUS %" PRId64 ".\n",
- dev->pci_bridge->bus.number);
+ dev->pci_bridge->bus->number);
monitor_printf(mon, " secondary bus %" PRId64 ".\n",
- dev->pci_bridge->bus.secondary);
+ dev->pci_bridge->bus->secondary);
monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
- dev->pci_bridge->bus.subordinate);
+ dev->pci_bridge->bus->subordinate);
monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
- dev->pci_bridge->bus.io_range->base,
- dev->pci_bridge->bus.io_range->limit);
+ dev->pci_bridge->bus->io_range->base,
+ dev->pci_bridge->bus->io_range->limit);
monitor_printf(mon,
" memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
- dev->pci_bridge->bus.memory_range->base,
- dev->pci_bridge->bus.memory_range->limit);
+ dev->pci_bridge->bus->memory_range->base,
+ dev->pci_bridge->bus->memory_range->limit);
monitor_printf(mon, " prefetchable memory range "
"[0x%08"PRIx64", 0x%08"PRIx64"]\n",
- dev->pci_bridge->bus.prefetchable_range->base,
- dev->pci_bridge->bus.prefetchable_range->limit);
+ dev->pci_bridge->bus->prefetchable_range->base,
+ dev->pci_bridge->bus->prefetchable_range->limit);
}
for (region = dev->regions; region; region = region->next) {
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index b3d5100112..f5c7a99717 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1456,24 +1456,26 @@ static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus,
int bus_num)
{
PciBridgeInfo *info;
+ PciMemoryRange *range;
- info = g_malloc0(sizeof(*info));
+ info = g_new0(PciBridgeInfo, 1);
- info->bus.number = dev->config[PCI_PRIMARY_BUS];
- info->bus.secondary = dev->config[PCI_SECONDARY_BUS];
- info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS];
+ info->bus = g_new0(PciBusInfo, 1);
+ info->bus->number = dev->config[PCI_PRIMARY_BUS];
+ info->bus->secondary = dev->config[PCI_SECONDARY_BUS];
+ info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS];
- info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range));
- info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
- info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
+ range = info->bus->io_range = g_new0(PciMemoryRange, 1);
+ range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
+ range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
- info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range));
- info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
- info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ range = info->bus->memory_range = g_new0(PciMemoryRange, 1);
+ range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
- info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range));
- info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
- info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+ range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1);
+ range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+ range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
if (dev->config[PCI_SECONDARY_BUS] != 0) {
PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]);
@@ -1494,21 +1496,23 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus,
uint8_t type;
int class;
- info = g_malloc0(sizeof(*info));
+ info = g_new0(PciDeviceInfo, 1);
info->bus = bus_num;
info->slot = PCI_SLOT(dev->devfn);
info->function = PCI_FUNC(dev->devfn);
+ info->class_info = g_new0(PciDeviceClass, 1);
class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
- info->class_info.q_class = class;
+ info->class_info->q_class = class;
desc = get_class_desc(class);
if (desc->desc) {
- info->class_info.has_desc = true;
- info->class_info.desc = g_strdup(desc->desc);
+ info->class_info->has_desc = true;
+ info->class_info->desc = g_strdup(desc->desc);
}
- info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
- info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID);
+ info->id = g_new0(PciDeviceId, 1);
+ info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
+ info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID);
info->regions = qmp_query_pci_regions(dev);
info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : "");
diff --git a/qapi-schema.json b/qapi-schema.json
index ac9594d66d..27ec9882db 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -71,7 +71,7 @@
#
# Since 0.14.0
##
-{ 'type': 'NameInfo', 'data': {'*name': 'str'} }
+{ 'struct': 'NameInfo', 'data': {'*name': 'str'} }
##
# @query-name:
@@ -95,7 +95,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} }
+{ 'struct': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} }
##
# @query-kvm:
@@ -170,7 +170,7 @@
#
# Notes: @singlestep is enabled through the GDB stub
##
-{ 'type': 'StatusInfo',
+{ 'struct': 'StatusInfo',
'data': {'running': 'bool', 'singlestep': 'bool', 'status': 'RunState'} }
##
@@ -195,7 +195,7 @@
#
# Notes: If no UUID was specified for the guest, a null UUID is returned.
##
-{ 'type': 'UuidInfo', 'data': {'UUID': 'str'} }
+{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} }
##
# @query-uuid:
@@ -226,7 +226,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'ChardevInfo', 'data': {'label': 'str',
+{ 'struct': 'ChardevInfo', 'data': {'label': 'str',
'filename': 'str',
'frontend-open': 'bool'} }
@@ -250,7 +250,7 @@
#
# Since: 2.0
##
-{ 'type': 'ChardevBackendInfo', 'data': {'name': 'str'} }
+{ 'struct': 'ChardevBackendInfo', 'data': {'name': 'str'} }
##
# @query-chardev-backends:
@@ -339,7 +339,7 @@
#
# Since: 1.2.0
##
-{ 'type': 'EventInfo', 'data': {'name': 'str'} }
+{ 'struct': 'EventInfo', 'data': {'name': 'str'} }
##
# @query-events:
@@ -380,7 +380,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'MigrationStats',
+{ 'struct': 'MigrationStats',
'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
'duplicate': 'int', 'skipped': 'int', 'normal': 'int',
'normal-bytes': 'int', 'dirty-pages-rate' : 'int',
@@ -405,7 +405,7 @@
#
# Since: 1.2
##
-{ 'type': 'XBZRLECacheStats',
+{ 'struct': 'XBZRLECacheStats',
'data': {'cache-size': 'int', 'bytes': 'int', 'pages': 'int',
'cache-miss': 'int', 'cache-miss-rate': 'number',
'overflow': 'int' } }
@@ -476,7 +476,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'MigrationInfo',
+{ 'struct': 'MigrationInfo',
'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats',
'*disk': 'MigrationStats',
'*xbzrle-cache': 'XBZRLECacheStats',
@@ -534,7 +534,7 @@
#
# Since: 1.2
##
-{ 'type': 'MigrationCapabilityStatus',
+{ 'struct': 'MigrationCapabilityStatus',
'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
##
@@ -575,7 +575,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'MouseInfo',
+{ 'struct': 'MouseInfo',
'data': {'name': 'str', 'index': 'int', 'current': 'bool',
'absolute': 'bool'} }
@@ -621,7 +621,7 @@
# Notes: @halted is a transient state that changes frequently. By the time the
# data is sent to the client, the guest may no longer be halted.
##
-{ 'type': 'CpuInfo',
+{ 'struct': 'CpuInfo',
'data': {'CPU': 'int', 'current': 'bool', 'halted': 'bool', '*pc': 'int',
'*nip': 'int', '*npc': 'int', '*PC': 'int', 'thread_id': 'int'} }
@@ -647,7 +647,7 @@
#
# Since: 2.0
##
-{ 'type': 'IOThreadInfo',
+{ 'struct': 'IOThreadInfo',
'data': {'id': 'str', 'thread-id': 'int'} }
##
@@ -700,7 +700,7 @@
#
# Since: 2.1
##
-{ 'type': 'VncBasicInfo',
+{ 'struct': 'VncBasicInfo',
'data': { 'host': 'str',
'service': 'str',
'family': 'NetworkAddressFamily',
@@ -715,7 +715,7 @@
#
# Since: 2.1
##
-{ 'type': 'VncServerInfo',
+{ 'struct': 'VncServerInfo',
'base': 'VncBasicInfo',
'data': { '*auth': 'str' } }
@@ -732,7 +732,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'VncClientInfo',
+{ 'struct': 'VncClientInfo',
'base': 'VncBasicInfo',
'data': { '*x509_dname': 'str', '*sasl_username': 'str' } }
@@ -772,7 +772,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'VncInfo',
+{ 'struct': 'VncInfo',
'data': {'enabled': 'bool', '*host': 'str',
'*family': 'NetworkAddressFamily',
'*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
@@ -826,7 +826,7 @@
#
# Since: 2.3
##
-{ 'type': 'VncInfo2',
+{ 'struct': 'VncInfo2',
'data': { 'id' : 'str',
'server' : ['VncBasicInfo'],
'clients' : ['VncClientInfo'],
@@ -869,7 +869,7 @@
#
# Since: 2.1
##
-{ 'type': 'SpiceBasicInfo',
+{ 'struct': 'SpiceBasicInfo',
'data': { 'host': 'str',
'port': 'str',
'family': 'NetworkAddressFamily' } }
@@ -883,7 +883,7 @@
#
# Since: 2.1
##
-{ 'type': 'SpiceServerInfo',
+{ 'struct': 'SpiceServerInfo',
'base': 'SpiceBasicInfo',
'data': { '*auth': 'str' } }
@@ -907,7 +907,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'SpiceChannel',
+{ 'struct': 'SpiceChannel',
'base': 'SpiceBasicInfo',
'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
'tls': 'bool'} }
@@ -965,7 +965,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'SpiceInfo',
+{ 'struct': 'SpiceInfo',
'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
'*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
@@ -991,7 +991,7 @@
# Since: 0.14.0
#
##
-{ 'type': 'BalloonInfo', 'data': {'actual': 'int' } }
+{ 'struct': 'BalloonInfo', 'data': {'actual': 'int' } }
##
# @query-balloon:
@@ -1018,7 +1018,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'PciMemoryRange', 'data': {'base': 'int', 'limit': 'int'} }
+{ 'struct': 'PciMemoryRange', 'data': {'base': 'int', 'limit': 'int'} }
##
# @PciMemoryRegion
@@ -1036,41 +1036,80 @@
#
# Since: 0.14.0
##
-{ 'type': 'PciMemoryRegion',
+{ 'struct': 'PciMemoryRegion',
'data': {'bar': 'int', 'type': 'str', 'address': 'int', 'size': 'int',
'*prefetch': 'bool', '*mem_type_64': 'bool' } }
##
-# @PciBridgeInfo:
+# @PciBusInfo:
#
-# Information about a PCI Bridge device
+# Information about a bus of a PCI Bridge device
#
-# @bus.number: primary bus interface number. This should be the number of the
-# bus the device resides on.
+# @number: primary bus interface number. This should be the number of the
+# bus the device resides on.
#
-# @bus.secondary: secondary bus interface number. This is the number of the
-# main bus for the bridge
+# @secondary: secondary bus interface number. This is the number of the
+# main bus for the bridge
#
-# @bus.subordinate: This is the highest number bus that resides below the
-# bridge.
+# @subordinate: This is the highest number bus that resides below the
+# bridge.
#
-# @bus.io_range: The PIO range for all devices on this bridge
+# @io_range: The PIO range for all devices on this bridge
#
-# @bus.memory_range: The MMIO range for all devices on this bridge
+# @memory_range: The MMIO range for all devices on this bridge
#
-# @bus.prefetchable_range: The range of prefetchable MMIO for all devices on
-# this bridge
+# @prefetchable_range: The range of prefetchable MMIO for all devices on
+# this bridge
+#
+# Since: 2.4
+##
+{ 'struct': 'PciBusInfo',
+ 'data': {'number': 'int', 'secondary': 'int', 'subordinate': 'int',
+ 'io_range': 'PciMemoryRange',
+ 'memory_range': 'PciMemoryRange',
+ 'prefetchable_range': 'PciMemoryRange' } }
+
+##
+# @PciBridgeInfo:
+#
+# Information about a PCI Bridge device
+#
+# @bus: information about the bus the device resides on
#
# @devices: a list of @PciDeviceInfo for each device on this bridge
#
# Since: 0.14.0
##
-{ 'type': 'PciBridgeInfo',
- 'data': {'bus': { 'number': 'int', 'secondary': 'int', 'subordinate': 'int',
- 'io_range': 'PciMemoryRange',
- 'memory_range': 'PciMemoryRange',
- 'prefetchable_range': 'PciMemoryRange' },
- '*devices': ['PciDeviceInfo']} }
+{ 'struct': 'PciBridgeInfo',
+ 'data': {'bus': 'PciBusInfo', '*devices': ['PciDeviceInfo']} }
+
+##
+# @PciDeviceClass:
+#
+# Information about the Class of a PCI device
+#
+# @desc: #optional a string description of the device's class
+#
+# @class: the class code of the device
+#
+# Since: 2.4
+##
+{ 'struct': 'PciDeviceClass',
+ 'data': {'*desc': 'str', 'class': 'int'} }
+
+##
+# @PciDeviceId:
+#
+# Information about the Id of a PCI device
+#
+# @device: the PCI device id
+#
+# @vendor: the PCI vendor id
+#
+# Since: 2.4
+##
+{ 'struct': 'PciDeviceId',
+ 'data': {'device': 'int', 'vendor': 'int'} }
##
# @PciDeviceInfo:
@@ -1083,13 +1122,9 @@
#
# @function: the function of the slot used by the device
#
-# @class_info.desc: #optional a string description of the device's class
-#
-# @class_info.class: the class code of the device
-#
-# @id.device: the PCI device id
+# @class_info: the class of the device
#
-# @id.vendor: the PCI vendor id
+# @id: the PCI device id
#
# @irq: #optional if an IRQ is assigned to the device, the IRQ number
#
@@ -1104,10 +1139,9 @@
#
# Since: 0.14.0
##
-{ 'type': 'PciDeviceInfo',
+{ 'struct': 'PciDeviceInfo',
'data': {'bus': 'int', 'slot': 'int', 'function': 'int',
- 'class_info': {'*desc': 'str', 'class': 'int'},
- 'id': {'device': 'int', 'vendor': 'int'},
+ 'class_info': 'PciDeviceClass', 'id': 'PciDeviceId',
'*irq': 'int', 'qdev_id': 'str', '*pci_bridge': 'PciBridgeInfo',
'regions': ['PciMemoryRegion']} }
@@ -1122,7 +1156,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'PciInfo', 'data': {'bus': 'int', 'devices': ['PciDeviceInfo']} }
+{ 'struct': 'PciInfo', 'data': {'bus': 'int', 'devices': ['PciDeviceInfo']} }
##
# @query-pci:
@@ -1341,7 +1375,7 @@
#
# Since: 1.6
###
-{ 'type': 'Abort',
+{ 'struct': 'Abort',
'data': { } }
##
@@ -1506,7 +1540,7 @@
#
# Since: 1.2
##
-{ 'type': 'ObjectPropertyInfo',
+{ 'struct': 'ObjectPropertyInfo',
'data': { 'name': 'str', 'type': 'str' } }
##
@@ -1561,8 +1595,8 @@
##
{ 'command': 'qom-get',
'data': { 'path': 'str', 'property': 'str' },
- 'returns': 'visitor',
- 'gen': 'no' }
+ 'returns': '**',
+ 'gen': false }
##
# @qom-set:
@@ -1579,8 +1613,8 @@
# Since: 1.2
##
{ 'command': 'qom-set',
- 'data': { 'path': 'str', 'property': 'str', 'value': 'visitor' },
- 'gen': 'no' }
+ 'data': { 'path': 'str', 'property': 'str', 'value': '**' },
+ 'gen': false }
##
# @set_password:
@@ -1691,7 +1725,7 @@
#
# Notes: This command is experimental and may change syntax in future releases.
##
-{ 'type': 'ObjectTypeInfo',
+{ 'struct': 'ObjectTypeInfo',
'data': { 'name': 'str' } }
##
@@ -1723,7 +1757,7 @@
#
# Since: 1.2
##
-{ 'type': 'DevicePropertyInfo',
+{ 'struct': 'DevicePropertyInfo',
'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
##
@@ -1903,7 +1937,7 @@
#
# Since: 2.0
##
-{ 'type': 'DumpGuestMemoryCapability',
+{ 'struct': 'DumpGuestMemoryCapability',
'data': {
'formats': ['DumpGuestMemoryFormat'] } }
@@ -1943,7 +1977,7 @@
##
{ 'command': 'netdev_add',
'data': {'type': 'str', 'id': 'str', '*props': '**'},
- 'gen': 'no' }
+ 'gen': false }
##
# @netdev_del:
@@ -1976,8 +2010,8 @@
# Since: 2.0
##
{ 'command': 'object-add',
- 'data': {'qom-type': 'str', 'id': 'str', '*props': 'dict'},
- 'gen': 'no' }
+ 'data': {'qom-type': 'str', 'id': 'str', '*props': '**'},
+ 'gen': false }
##
# @object-del:
@@ -2000,7 +2034,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevNoneOptions',
+{ 'struct': 'NetdevNoneOptions',
'data': { } }
##
@@ -2020,7 +2054,7 @@
#
# Since 1.2
##
-{ 'type': 'NetLegacyNicOptions',
+{ 'struct': 'NetLegacyNicOptions',
'data': {
'*netdev': 'str',
'*macaddr': 'str',
@@ -2035,7 +2069,7 @@
#
# Since 1.2
##
-{ 'type': 'String',
+{ 'struct': 'String',
'data': {
'str': 'str' } }
@@ -2078,7 +2112,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevUserOptions',
+{ 'struct': 'NetdevUserOptions',
'data': {
'*hostname': 'str',
'*restrict': 'bool',
@@ -2130,7 +2164,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevTapOptions',
+{ 'struct': 'NetdevTapOptions',
'data': {
'*ifname': 'str',
'*fd': 'str',
@@ -2166,7 +2200,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevSocketOptions',
+{ 'struct': 'NetdevSocketOptions',
'data': {
'*fd': 'str',
'*listen': 'str',
@@ -2214,7 +2248,7 @@
#
# Since 2.1
##
-{ 'type': 'NetdevL2TPv3Options',
+{ 'struct': 'NetdevL2TPv3Options',
'data': {
'src': 'str',
'dst': 'str',
@@ -2246,7 +2280,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevVdeOptions',
+{ 'struct': 'NetdevVdeOptions',
'data': {
'*sock': 'str',
'*port': 'uint16',
@@ -2265,7 +2299,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevDumpOptions',
+{ 'struct': 'NetdevDumpOptions',
'data': {
'*len': 'size',
'*file': 'str' } }
@@ -2281,7 +2315,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevBridgeOptions',
+{ 'struct': 'NetdevBridgeOptions',
'data': {
'*br': 'str',
'*helper': 'str' } }
@@ -2295,7 +2329,7 @@
#
# Since 1.2
##
-{ 'type': 'NetdevHubPortOptions',
+{ 'struct': 'NetdevHubPortOptions',
'data': {
'hubid': 'int32' } }
@@ -2315,7 +2349,7 @@
#
# Since 2.0
##
-{ 'type': 'NetdevNetmapOptions',
+{ 'struct': 'NetdevNetmapOptions',
'data': {
'ifname': 'str',
'*devname': 'str' } }
@@ -2331,7 +2365,7 @@
#
# Since 2.1
##
-{ 'type': 'NetdevVhostUserOptions',
+{ 'struct': 'NetdevVhostUserOptions',
'data': {
'chardev': 'str',
'*vhostforce': 'bool' } }
@@ -2376,7 +2410,7 @@
#
# Since 1.2
##
-{ 'type': 'NetLegacy',
+{ 'struct': 'NetLegacy',
'data': {
'*vlan': 'int32',
'*id': 'str',
@@ -2394,7 +2428,7 @@
#
# Since 1.2
##
-{ 'type': 'Netdev',
+{ 'struct': 'Netdev',
'data': {
'id': 'str',
'opts': 'NetClientOptions' } }
@@ -2418,7 +2452,7 @@
#
# Since 1.3
##
-{ 'type': 'InetSocketAddress',
+{ 'struct': 'InetSocketAddress',
'data': {
'host': 'str',
'port': 'str',
@@ -2435,7 +2469,7 @@
#
# Since 1.3
##
-{ 'type': 'UnixSocketAddress',
+{ 'struct': 'UnixSocketAddress',
'data': {
'path': 'str' } }
@@ -2500,7 +2534,7 @@
#
# Since: 1.2.0
##
-{ 'type': 'MachineInfo',
+{ 'struct': 'MachineInfo',
'data': { 'name': 'str', '*alias': 'str',
'*is-default': 'bool', 'cpu-max': 'int' } }
@@ -2524,7 +2558,7 @@
#
# Since: 1.2.0
##
-{ 'type': 'CpuDefinitionInfo',
+{ 'struct': 'CpuDefinitionInfo',
'data': { 'name': 'str' } }
##
@@ -2549,7 +2583,7 @@
#
# Since: 1.2.0
##
-{ 'type': 'AddfdInfo', 'data': {'fdset-id': 'int', 'fd': 'int'} }
+{ 'struct': 'AddfdInfo', 'data': {'fdset-id': 'int', 'fd': 'int'} }
##
# @add-fd:
@@ -2605,7 +2639,7 @@
#
# Since: 1.2.0
##
-{ 'type': 'FdsetFdInfo',
+{ 'struct': 'FdsetFdInfo',
'data': {'fd': 'int', '*opaque': 'str'} }
##
@@ -2619,7 +2653,7 @@
#
# Since: 1.2.0
##
-{ 'type': 'FdsetInfo',
+{ 'struct': 'FdsetInfo',
'data': {'fdset-id': 'int', 'fds': ['FdsetFdInfo']} }
##
@@ -2645,7 +2679,7 @@
#
# Since: 1.2.0
##
-{ 'type': 'TargetInfo',
+{ 'struct': 'TargetInfo',
'data': { 'arch': 'str' } }
##
@@ -2745,7 +2779,7 @@
#
# Since: 1.4
##
-{ 'type': 'ChardevFile', 'data': { '*in' : 'str',
+{ 'struct': 'ChardevFile', 'data': { '*in' : 'str',
'out' : 'str' } }
##
@@ -2759,7 +2793,7 @@
#
# Since: 1.4
##
-{ 'type': 'ChardevHostdev', 'data': { 'device' : 'str' } }
+{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' } }
##
# @ChardevSocket:
@@ -2781,7 +2815,7 @@
#
# Since: 1.4
##
-{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
+{ 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
'*server' : 'bool',
'*wait' : 'bool',
'*nodelay' : 'bool',
@@ -2798,7 +2832,7 @@
#
# Since: 1.5
##
-{ 'type': 'ChardevUdp', 'data': { 'remote' : 'SocketAddress',
+{ 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddress',
'*local' : 'SocketAddress' } }
##
@@ -2810,7 +2844,7 @@
#
# Since: 1.5
##
-{ 'type': 'ChardevMux', 'data': { 'chardev' : 'str' } }
+{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' } }
##
# @ChardevStdio:
@@ -2823,7 +2857,7 @@
#
# Since: 1.5
##
-{ 'type': 'ChardevStdio', 'data': { '*signal' : 'bool' } }
+{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' } }
##
# @ChardevSpiceChannel:
@@ -2834,7 +2868,7 @@
#
# Since: 1.5
##
-{ 'type': 'ChardevSpiceChannel', 'data': { 'type' : 'str' } }
+{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' } }
##
# @ChardevSpicePort:
@@ -2845,7 +2879,7 @@
#
# Since: 1.5
##
-{ 'type': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' } }
+{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' } }
##
# @ChardevVC:
@@ -2859,7 +2893,7 @@
#
# Since: 1.5
##
-{ 'type': 'ChardevVC', 'data': { '*width' : 'int',
+{ 'struct': 'ChardevVC', 'data': { '*width' : 'int',
'*height' : 'int',
'*cols' : 'int',
'*rows' : 'int' } }
@@ -2873,7 +2907,7 @@
#
# Since: 1.5
##
-{ 'type': 'ChardevRingbuf', 'data': { '*size' : 'int' } }
+{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' } }
##
# @ChardevBackend:
@@ -2882,7 +2916,7 @@
#
# Since: 1.4 (testdev since 2.2)
##
-{ 'type': 'ChardevDummy', 'data': { } }
+{ 'struct': 'ChardevDummy', 'data': { } }
{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile',
'serial' : 'ChardevHostdev',
@@ -2915,7 +2949,7 @@
#
# Since: 1.4
##
-{ 'type' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
+{ 'struct' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
##
# @chardev-add:
@@ -3002,7 +3036,7 @@
#
# Since: 1.5
##
-{ 'type': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
+{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
'*cancel-path' : 'str'} }
##
@@ -3030,7 +3064,7 @@
#
# Since: 1.5
##
-{ 'type': 'TPMInfo',
+{ 'struct': 'TPMInfo',
'data': {'id': 'str',
'model': 'TpmModel',
'options': 'TpmTypeOptions' } }
@@ -3092,7 +3126,7 @@
#
# Since 1.5
##
-{ 'type': 'AcpiTableOptions',
+{ 'struct': 'AcpiTableOptions',
'data': {
'*sig': 'str',
'*rev': 'uint8',
@@ -3138,7 +3172,7 @@
#
# Since 1.5
##
-{ 'type': 'CommandLineParameterInfo',
+{ 'struct': 'CommandLineParameterInfo',
'data': { 'name': 'str',
'type': 'CommandLineParameterType',
'*help': 'str',
@@ -3155,7 +3189,7 @@
#
# Since 1.5
##
-{ 'type': 'CommandLineOptionInfo',
+{ 'struct': 'CommandLineOptionInfo',
'data': { 'option': 'str', 'parameters': ['CommandLineParameterInfo'] } }
##
@@ -3199,7 +3233,7 @@
#
# Since: 1.5
##
-{ 'type': 'X86CPUFeatureWordInfo',
+{ 'struct': 'X86CPUFeatureWordInfo',
'data': { 'cpuid-input-eax': 'int',
'*cpuid-input-ecx': 'int',
'cpuid-register': 'X86CPURegister32',
@@ -3252,7 +3286,7 @@
# Since 1.6
##
-{ 'type': 'RxFilterInfo',
+{ 'struct': 'RxFilterInfo',
'data': {
'name': 'str',
'promiscuous': 'bool',
@@ -3314,7 +3348,7 @@
#
# Since: 2.0
##
-{ 'type' : 'InputKeyEvent',
+{ 'struct' : 'InputKeyEvent',
'data' : { 'key' : 'KeyValue',
'down' : 'bool' } }
@@ -3328,7 +3362,7 @@
#
# Since: 2.0
##
-{ 'type' : 'InputBtnEvent',
+{ 'struct' : 'InputBtnEvent',
'data' : { 'button' : 'InputButton',
'down' : 'bool' } }
@@ -3343,7 +3377,7 @@
#
# Since: 2.0
##
-{ 'type' : 'InputMoveEvent',
+{ 'struct' : 'InputMoveEvent',
'data' : { 'axis' : 'InputAxis',
'value' : 'int' } }
@@ -3426,7 +3460,7 @@
#
# Since: 2.1
##
-{ 'type': 'NumaNodeOptions',
+{ 'struct': 'NumaNodeOptions',
'data': {
'*nodeid': 'uint16',
'*cpus': ['uint16'],
@@ -3473,7 +3507,7 @@
# Since: 2.1
##
-{ 'type': 'Memdev',
+{ 'struct': 'Memdev',
'data': {
'size': 'size',
'merge': 'bool',
@@ -3516,7 +3550,7 @@
#
# Since: 2.1
##
-{ 'type': 'PCDIMMDeviceInfo',
+{ 'struct': 'PCDIMMDeviceInfo',
'data': { '*id': 'str',
'addr': 'int',
'size': 'int',
@@ -3570,7 +3604,7 @@
#
# Since: 2.1
##
-{ 'type': 'ACPIOSTInfo',
+{ 'struct': 'ACPIOSTInfo',
'data' : { '*device': 'str',
'slot': 'str',
'slot-type': 'ACPISlotType',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 1c17224c77..dcf7c04ffa 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -26,7 +26,7 @@
#
##
-{ 'type': 'SnapshotInfo',
+{ 'struct': 'SnapshotInfo',
'data': { 'id': 'str', 'name': 'str', 'vm-state-size': 'int',
'date-sec': 'int', 'date-nsec': 'int',
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
@@ -45,7 +45,7 @@
#
# Since: 1.7
##
-{ 'type': 'ImageInfoSpecificQCow2',
+{ 'struct': 'ImageInfoSpecificQCow2',
'data': {
'compat': 'str',
'*lazy-refcounts': 'bool',
@@ -66,7 +66,7 @@
#
# Since: 1.7
##
-{ 'type': 'ImageInfoSpecificVmdk',
+{ 'struct': 'ImageInfoSpecificVmdk',
'data': {
'create-type': 'str',
'cid': 'int',
@@ -126,7 +126,7 @@
#
##
-{ 'type': 'ImageInfo',
+{ 'struct': 'ImageInfo',
'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool',
'*actual-size': 'int', 'virtual-size': 'int',
'*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool',
@@ -178,7 +178,7 @@
#
##
-{ 'type': 'ImageCheck',
+{ 'struct': 'ImageCheck',
'data': {'filename': 'str', 'format': 'str', 'check-errors': 'int',
'*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'int',
'*corruptions-fixed': 'int', '*leaks-fixed': 'int',
@@ -196,7 +196,7 @@
#
# Since: 2.3
##
-{ 'type': 'BlockdevCacheInfo',
+{ 'struct': 'BlockdevCacheInfo',
'data': { 'writeback': 'bool',
'direct': 'bool',
'no-flush': 'bool' } }
@@ -267,7 +267,7 @@
# Since: 0.14.0
#
##
-{ 'type': 'BlockDeviceInfo',
+{ 'struct': 'BlockDeviceInfo',
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
'*backing_file': 'str', 'backing_file_depth': 'int',
'encrypted': 'bool', 'encryption_key_missing': 'bool',
@@ -321,7 +321,7 @@
#
# Since 1.7
##
-{ 'type': 'BlockDeviceMapEntry',
+{ 'struct': 'BlockDeviceMapEntry',
'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool',
'data': 'bool', '*offset': 'int' } }
@@ -340,7 +340,7 @@
#
# Since: 1.3
##
-{ 'type': 'BlockDirtyInfo',
+{ 'struct': 'BlockDirtyInfo',
'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32',
'frozen': 'bool'} }
@@ -375,7 +375,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'BlockInfo',
+{ 'struct': 'BlockInfo',
'data': {'device': 'str', 'type': 'str', 'removable': 'bool',
'locked': 'bool', '*inserted': 'BlockDeviceInfo',
'*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus',
@@ -428,7 +428,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'BlockDeviceStats',
+{ 'struct': 'BlockDeviceStats',
'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int',
'wr_operations': 'int', 'flush_operations': 'int',
'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
@@ -454,7 +454,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'BlockStats',
+{ 'struct': 'BlockStats',
'data': {'*device': 'str', '*node-name': 'str',
'stats': 'BlockDeviceStats',
'*parent': 'BlockStats',
@@ -567,7 +567,7 @@
#
# Since: 1.1
##
-{ 'type': 'BlockJobInfo',
+{ 'struct': 'BlockJobInfo',
'data': {'type': 'str', 'device': 'str', 'len': 'int',
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} }
@@ -677,7 +677,7 @@
# @mode: #optional whether and how QEMU should create a new image, default is
# 'absolute-paths'.
##
-{ 'type': 'BlockdevSnapshot',
+{ 'struct': 'BlockdevSnapshot',
'data': { '*device': 'str', '*node-name': 'str',
'snapshot-file': 'str', '*snapshot-node-name': 'str',
'*format': 'str', '*mode': 'NewImageMode' } }
@@ -721,7 +721,7 @@
#
# Since: 1.6
##
-{ 'type': 'DriveBackup',
+{ 'struct': 'DriveBackup',
'data': { 'device': 'str', 'target': 'str', '*format': 'str',
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
'*speed': 'int', '*bitmap': 'str',
@@ -756,7 +756,7 @@
#
# Since: 2.3
##
-{ 'type': 'BlockdevBackup',
+{ 'struct': 'BlockdevBackup',
'data': { 'device': 'str', 'target': 'str',
'sync': 'MirrorSyncMode',
'*speed': 'int',
@@ -977,7 +977,7 @@
#
# Since 2.4
##
-{ 'type': 'BlockDirtyBitmap',
+{ 'struct': 'BlockDirtyBitmap',
'data': { 'node': 'str', 'name': 'str' } }
##
@@ -992,7 +992,7 @@
#
# Since 2.4
##
-{ 'type': 'BlockDirtyBitmapAdd',
+{ 'struct': 'BlockDirtyBitmapAdd',
'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32' } }
##
@@ -1313,7 +1313,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevCacheOptions',
+{ 'struct': 'BlockdevCacheOptions',
'data': { '*writeback': 'bool',
'*direct': 'bool',
'*no-flush': 'bool' } }
@@ -1360,7 +1360,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevOptionsBase',
+{ 'struct': 'BlockdevOptionsBase',
'data': { 'driver': 'BlockdevDriver',
'*id': 'str',
'*node-name': 'str',
@@ -1382,7 +1382,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevOptionsFile',
+{ 'struct': 'BlockdevOptionsFile',
'data': { 'filename': 'str' } }
##
@@ -1397,7 +1397,7 @@
#
# Since: 2.2
##
-{ 'type': 'BlockdevOptionsNull',
+{ 'struct': 'BlockdevOptionsNull',
'data': { '*size': 'int', '*latency-ns': 'uint64' } }
##
@@ -1413,7 +1413,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevOptionsVVFAT',
+{ 'struct': 'BlockdevOptionsVVFAT',
'data': { 'dir': 'str', '*fat-type': 'int', '*floppy': 'bool',
'*rw': 'bool' } }
@@ -1427,7 +1427,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevOptionsGenericFormat',
+{ 'struct': 'BlockdevOptionsGenericFormat',
'data': { 'file': 'BlockdevRef' } }
##
@@ -1443,7 +1443,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevOptionsGenericCOWFormat',
+{ 'struct': 'BlockdevOptionsGenericCOWFormat',
'base': 'BlockdevOptionsGenericFormat',
'data': { '*backing': 'BlockdevRef' } }
@@ -1479,7 +1479,7 @@
#
# Since: 2.2
##
-{ 'type': 'Qcow2OverlapCheckFlags',
+{ 'struct': 'Qcow2OverlapCheckFlags',
'data': { '*template': 'Qcow2OverlapCheckMode',
'*main-header': 'bool',
'*active-l1': 'bool',
@@ -1503,8 +1503,7 @@
#
# Since: 2.2
##
-{ 'union': 'Qcow2OverlapChecks',
- 'discriminator': {},
+{ 'alternate': 'Qcow2OverlapChecks',
'data': { 'flags': 'Qcow2OverlapCheckFlags',
'mode': 'Qcow2OverlapCheckMode' } }
@@ -1541,7 +1540,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevOptionsQcow2',
+{ 'struct': 'BlockdevOptionsQcow2',
'base': 'BlockdevOptionsGenericCOWFormat',
'data': { '*lazy-refcounts': 'bool',
'*pass-discard-request': 'bool',
@@ -1576,7 +1575,7 @@
# use the default value, 'archipelago'.
# Since: 2.2
##
-{ 'type': 'BlockdevOptionsArchipelago',
+{ 'struct': 'BlockdevOptionsArchipelago',
'data': { 'volume': 'str',
'*mport': 'int',
'*vport': 'int',
@@ -1628,7 +1627,7 @@
#
# Since: 2.0
##
-{ 'type': 'BlkdebugInjectErrorOptions',
+{ 'struct': 'BlkdebugInjectErrorOptions',
'data': { 'event': 'BlkdebugEvent',
'*state': 'int',
'*errno': 'int',
@@ -1651,7 +1650,7 @@
#
# Since: 2.0
##
-{ 'type': 'BlkdebugSetStateOptions',
+{ 'struct': 'BlkdebugSetStateOptions',
'data': { 'event': 'BlkdebugEvent',
'*state': 'int',
'new_state': 'int' } }
@@ -1673,7 +1672,7 @@
#
# Since: 2.0
##
-{ 'type': 'BlockdevOptionsBlkdebug',
+{ 'struct': 'BlockdevOptionsBlkdebug',
'data': { 'image': 'BlockdevRef',
'*config': 'str',
'*align': 'int',
@@ -1691,7 +1690,7 @@
#
# Since: 2.0
##
-{ 'type': 'BlockdevOptionsBlkverify',
+{ 'struct': 'BlockdevOptionsBlkverify',
'data': { 'test': 'BlockdevRef',
'raw': 'BlockdevRef' } }
@@ -1728,7 +1727,7 @@
#
# Since: 2.0
##
-{ 'type': 'BlockdevOptionsQuorum',
+{ 'struct': 'BlockdevOptionsQuorum',
'data': { '*blkverify': 'bool',
'children': [ 'BlockdevRef' ],
'vote-threshold': 'int',
@@ -1795,8 +1794,7 @@
#
# Since: 1.7
##
-{ 'union': 'BlockdevRef',
- 'discriminator': {},
+{ 'alternate': 'BlockdevRef',
'data': { 'definition': 'BlockdevOptions',
'reference': 'str' } }
diff --git a/qapi/block.json b/qapi/block.json
index e3134657b6..aad645c4a6 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -52,7 +52,7 @@
#
# Since: 1.7
##
-{ 'type': 'BlockdevSnapshotInternal',
+{ 'struct': 'BlockdevSnapshotInternal',
'data': { 'device': 'str', 'name': 'str' } }
##
diff --git a/qapi/common.json b/qapi/common.json
index 63ef3b4724..bad56bf688 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -29,15 +29,28 @@
'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
##
-# @VersionInfo:
+# @VersionTriple
#
-# A description of QEMU's version.
+# A three-part version number.
+#
+# @qemu.major: The major version number.
#
-# @qemu.major: The major version of QEMU
+# @qemu.minor: The minor version number.
#
-# @qemu.minor: The minor version of QEMU
+# @qemu.micro: The micro version number.
+#
+# Since: 2.4
+##
+{ 'struct': 'VersionTriple',
+ 'data': {'major': 'int', 'minor': 'int', 'micro': 'int'} }
+
+
+##
+# @VersionInfo:
+#
+# A description of QEMU's version.
#
-# @qemu.micro: The micro version of QEMU. By current convention, a micro
+# @qemu: The version of QEMU. By current convention, a micro
# version of 50 signifies a development branch. A micro version
# greater than or equal to 90 signifies a release candidate for
# the next minor version. A micro version of less than 50
@@ -50,9 +63,8 @@
#
# Since: 0.14.0
##
-{ 'type': 'VersionInfo',
- 'data': {'qemu': {'major': 'int', 'minor': 'int', 'micro': 'int'},
- 'package': 'str'} }
+{ 'struct': 'VersionInfo',
+ 'data': {'qemu': 'VersionTriple', 'package': 'str'} }
##
# @query-version:
@@ -74,7 +86,7 @@
#
# Since: 0.14.0
##
-{ 'type': 'CommandInfo', 'data': {'name': 'str'} }
+{ 'struct': 'CommandInfo', 'data': {'name': 'str'} }
##
# @query-commands:
diff --git a/qapi/trace.json b/qapi/trace.json
index 06c613c213..01b0a52a7e 100644
--- a/qapi/trace.json
+++ b/qapi/trace.json
@@ -32,7 +32,7 @@
#
# Since 2.2
##
-{ 'type': 'TraceEventInfo',
+{ 'struct': 'TraceEventInfo',
'data': {'name': 'str', 'state': 'TraceEventState'} }
##
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 5c4cd40a92..b446dc729d 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -150,7 +150,7 @@
#
# Since 1.1.0
##
-{ 'type': 'GuestAgentCommandInfo',
+{ 'struct': 'GuestAgentCommandInfo',
'data': { 'name': 'str', 'enabled': 'bool', 'success-response': 'bool' } }
##
@@ -164,7 +164,7 @@
#
# Since 0.15.0
##
-{ 'type': 'GuestAgentInfo',
+{ 'struct': 'GuestAgentInfo',
'data': { 'version': 'str',
'supported_commands': ['GuestAgentCommandInfo'] } }
##
@@ -195,7 +195,7 @@
# Since: 0.15.0
##
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' },
- 'success-response': 'no' }
+ 'success-response': false }
##
# @guest-file-open:
@@ -242,7 +242,7 @@
#
# Since: 0.15.0
##
-{ 'type': 'GuestFileRead',
+{ 'struct': 'GuestFileRead',
'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
##
@@ -274,7 +274,7 @@
#
# Since: 0.15.0
##
-{ 'type': 'GuestFileWrite',
+{ 'struct': 'GuestFileWrite',
'data': { 'count': 'int', 'eof': 'bool' } }
##
@@ -309,7 +309,7 @@
#
# Since: 0.15.0
##
-{ 'type': 'GuestFileSeek',
+{ 'struct': 'GuestFileSeek',
'data': { 'position': 'int', 'eof': 'bool' } }
##
@@ -470,7 +470,7 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-disk', 'success-response': 'no' }
+{ 'command': 'guest-suspend-disk', 'success-response': false }
##
# @guest-suspend-ram
@@ -502,7 +502,7 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-ram', 'success-response': 'no' }
+{ 'command': 'guest-suspend-ram', 'success-response': false }
##
# @guest-suspend-hybrid
@@ -529,7 +529,7 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-hybrid', 'success-response': 'no' }
+{ 'command': 'guest-suspend-hybrid', 'success-response': false }
##
# @GuestIpAddressType:
@@ -556,7 +556,7 @@
#
# Since: 1.1
##
-{ 'type': 'GuestIpAddress',
+{ 'struct': 'GuestIpAddress',
'data': {'ip-address': 'str',
'ip-address-type': 'GuestIpAddressType',
'prefix': 'int'} }
@@ -572,7 +572,7 @@
#
# Since: 1.1
##
-{ 'type': 'GuestNetworkInterface',
+{ 'struct': 'GuestNetworkInterface',
'data': {'name': 'str',
'*hardware-address': 'str',
'*ip-addresses': ['GuestIpAddress'] } }
@@ -604,7 +604,7 @@
#
# Since: 1.5
##
-{ 'type': 'GuestLogicalProcessor',
+{ 'struct': 'GuestLogicalProcessor',
'data': {'logical-id': 'int',
'online': 'bool',
'*can-offline': 'bool'} }
@@ -694,7 +694,7 @@
#
# Since: 2.2
##
-{ 'type': 'GuestPCIAddress',
+{ 'struct': 'GuestPCIAddress',
'data': {'domain': 'int', 'bus': 'int',
'slot': 'int', 'function': 'int'} }
@@ -709,7 +709,7 @@
#
# Since: 2.2
##
-{ 'type': 'GuestDiskAddress',
+{ 'struct': 'GuestDiskAddress',
'data': {'pci-controller': 'GuestPCIAddress',
'bus-type': 'GuestDiskBusType',
'bus': 'int', 'target': 'int', 'unit': 'int'} }
@@ -725,7 +725,7 @@
#
# Since: 2.2
##
-{ 'type': 'GuestFilesystemInfo',
+{ 'struct': 'GuestFilesystemInfo',
'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
'disk': ['GuestDiskAddress']} }
@@ -782,7 +782,7 @@
#
# Since: 2.3
##
-{ 'type': 'GuestMemoryBlock',
+{ 'struct': 'GuestMemoryBlock',
'data': {'phys-index': 'uint64',
'online': 'bool',
'*can-offline': 'bool'} }
@@ -835,7 +835,7 @@
#
# Since: 2.3
##
-{ 'type': 'GuestMemoryBlockResponse',
+{ 'struct': 'GuestMemoryBlockResponse',
'data': { 'phys-index': 'uint64',
'response': 'GuestMemoryBlockResponseType',
'*error-code': 'int' }}
@@ -876,7 +876,7 @@
#
# Since: 2.3
##
-{ 'type': 'GuestMemoryBlockInfo',
+{ 'struct': 'GuestMemoryBlockInfo',
'data': {'size': 'uint64'} }
##
diff --git a/qmp.c b/qmp.c
index e6c70504df..3f5dfe3f51 100644
--- a/qmp.c
+++ b/qmp.c
@@ -45,15 +45,16 @@ NameInfo *qmp_query_name(Error **errp)
VersionInfo *qmp_query_version(Error **errp)
{
- VersionInfo *info = g_malloc0(sizeof(*info));
+ VersionInfo *info = g_new0(VersionInfo, 1);
const char *version = QEMU_VERSION;
char *tmp;
- info->qemu.major = strtol(version, &tmp, 10);
+ info->qemu = g_new0(VersionTriple, 1);
+ info->qemu->major = strtol(version, &tmp, 10);
tmp++;
- info->qemu.minor = strtol(tmp, &tmp, 10);
+ info->qemu->minor = strtol(tmp, &tmp, 10);
tmp++;
- info->qemu.micro = strtol(tmp, &tmp, 10);
+ info->qemu->micro = strtol(tmp, &tmp, 10);
info->package = g_strdup(QEMU_PKGVERSION);
return info;
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 053ba85b5f..93e43f0e48 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -2,7 +2,7 @@
# QAPI command marshaller generator
#
# Copyright IBM, Corp. 2011
-# Copyright (C) 2014 Red Hat, Inc.
+# Copyright (C) 2014-2015 Red Hat, Inc.
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
@@ -28,7 +28,7 @@ def type_visitor(name):
def generate_command_decl(name, args, ret_type):
arglist=""
- for argname, argtype, optional, structured in parse_args(args):
+ for argname, argtype, optional in parse_args(args):
argtype = c_type(argtype, is_param=True)
if optional:
arglist += "bool has_%s, " % c_var(argname)
@@ -53,7 +53,7 @@ def gen_sync_call(name, args, ret_type, indent=0):
retval=""
if ret_type:
retval = "retval = "
- for argname, argtype, optional, structured in parse_args(args):
+ for argname, argtype, optional in parse_args(args):
if optional:
arglist += "has_%s, " % c_var(argname)
arglist += "%s, " % (c_var(argname))
@@ -96,7 +96,7 @@ Visitor *v;
def gen_visitor_input_vars_decl(args):
ret = ""
push_indent()
- for argname, argtype, optional, structured in parse_args(args):
+ for argname, argtype, optional in parse_args(args):
if optional:
ret += mcgen('''
bool has_%(argname)s = false;
@@ -139,7 +139,7 @@ v = qapi_dealloc_get_visitor(md);
v = qmp_input_get_visitor(mi);
''')
- for argname, argtype, optional, structured in parse_args(args):
+ for argname, argtype, optional in parse_args(args):
if optional:
ret += mcgen('''
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
@@ -293,17 +293,12 @@ out:
return ret
-def option_value_matches(opt, val, cmd):
- if opt in cmd and cmd[opt] == val:
- return True
- return False
-
def gen_registry(commands):
registry=""
push_indent()
for cmd in commands:
options = 'QCO_NO_OPTIONS'
- if option_value_matches('success-response', 'no', cmd):
+ if not cmd.get('success-response', True):
options = 'QCO_NO_SUCCESS_RESP'
registry += mcgen('''
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 601e3076ab..47dc041805 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -21,7 +21,7 @@ def _generate_event_api_name(event_name, params):
l = len(api_name)
if params:
- for argname, argentry, optional, structured in parse_args(params):
+ for argname, argentry, optional in parse_args(params):
if optional:
api_name += "bool has_%s,\n" % c_var(argname)
api_name += "".ljust(l)
@@ -93,7 +93,7 @@ def generate_event_implement(api_name, event_name, params):
""",
event_name = event_name)
- for argname, argentry, optional, structured in parse_args(params):
+ for argname, argentry, optional in parse_args(params):
if optional:
ret += mcgen("""
if (has_%(var)s) {
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index db872180c6..2bf8145076 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -63,18 +63,13 @@ typedef struct %(name)sList
def generate_struct_fields(members):
ret = ''
- for argname, argentry, optional, structured in parse_args(members):
+ for argname, argentry, optional in parse_args(members):
if optional:
ret += mcgen('''
bool has_%(c_name)s;
''',
c_name=c_var(argname))
- if structured:
- push_indent()
- ret += generate_struct({ "field": argname, "data": argentry})
- pop_indent()
- else:
- ret += mcgen('''
+ ret += mcgen('''
%(c_type)s %(c_name)s;
''',
c_type=c_type(argentry), c_name=c_var(argname))
@@ -83,7 +78,7 @@ def generate_struct_fields(members):
def generate_struct(expr):
- structname = expr.get('type', "")
+ structname = expr.get('struct', "")
fieldname = expr.get('field', "")
members = expr['data']
base = expr.get('base')
@@ -170,9 +165,9 @@ typedef enum %(name)s
return lookup_decl + enum_decl
-def generate_anon_union_qtypes(expr):
+def generate_alternate_qtypes(expr):
- name = expr['union']
+ name = expr['alternate']
members = expr['data']
ret = mcgen('''
@@ -181,17 +176,8 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
name=name)
for key in members:
- qapi_type = members[key]
- if builtin_type_qtypes.has_key(qapi_type):
- qtype = builtin_type_qtypes[qapi_type]
- elif find_struct(qapi_type):
- qtype = "QTYPE_QDICT"
- elif find_union(qapi_type):
- qtype = "QTYPE_QDICT"
- elif find_enum(qapi_type):
- qtype = "QTYPE_QSTRING"
- else:
- assert False, "Invalid anonymous union member"
+ qtype = find_alternate_member_qtype(members[key])
+ assert qtype, "Invalid alternate member"
ret += mcgen('''
[ %(qtype)s ] = %(abbrev)s_KIND_%(enum)s,
@@ -206,9 +192,9 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
return ret
-def generate_union(expr):
+def generate_union(expr, meta):
- name = expr['union']
+ name = expr[meta]
typeinfo = expr['data']
base = expr.get('base')
@@ -242,10 +228,9 @@ struct %(name)s
''')
if base:
- base_fields = find_struct(base)['data']
- if discriminator:
- base_fields = base_fields.copy()
- del base_fields[discriminator]
+ assert discriminator
+ base_fields = find_struct(base)['data'].copy()
+ del base_fields[discriminator]
ret += generate_struct_fields(base_fields)
else:
assert not discriminator
@@ -253,7 +238,7 @@ struct %(name)s
ret += mcgen('''
};
''')
- if discriminator == {}:
+ if meta == 'alternate':
ret += mcgen('''
extern const int %(name)s_qtypes[];
''',
@@ -398,14 +383,14 @@ exprs = parse_schema(input_file)
exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
-for typename in builtin_types:
+for typename in builtin_types.keys():
fdecl.write(generate_fwd_struct(typename, None, builtin_type=True))
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
for expr in exprs:
ret = "\n"
- if expr.has_key('type'):
- ret += generate_fwd_struct(expr['type'], expr['data'])
+ if expr.has_key('struct'):
+ ret += generate_fwd_struct(expr['struct'], expr['data'])
elif expr.has_key('enum'):
ret += generate_enum(expr['enum'], expr['data']) + "\n"
ret += generate_fwd_enum_struct(expr['enum'], expr['data'])
@@ -417,8 +402,12 @@ for expr in exprs:
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
expr['data'].keys()))
- if expr.get('discriminator') == {}:
- fdef.write(generate_anon_union_qtypes(expr))
+ elif expr.has_key('alternate'):
+ ret += generate_fwd_struct(expr['alternate'], expr['data']) + "\n"
+ ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
+ fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
+ expr['data'].keys()))
+ fdef.write(generate_alternate_qtypes(expr))
else:
continue
fdecl.write(ret)
@@ -426,7 +415,7 @@ for expr in exprs:
# to avoid header dependency hell, we always generate declarations
# for built-in types in our header files and simply guard them
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
-for typename in builtin_types:
+for typename in builtin_types.keys():
fdecl.write(generate_type_cleanup_decl(typename + "List"))
fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
@@ -435,24 +424,30 @@ fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
# over these cases
if do_builtins:
fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
- for typename in builtin_types:
+ for typename in builtin_types.keys():
fdef.write(generate_type_cleanup(typename + "List"))
fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
for expr in exprs:
ret = "\n"
- if expr.has_key('type'):
+ if expr.has_key('struct'):
ret += generate_struct(expr) + "\n"
- ret += generate_type_cleanup_decl(expr['type'] + "List")
- fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n")
- ret += generate_type_cleanup_decl(expr['type'])
- fdef.write(generate_type_cleanup(expr['type']) + "\n")
+ ret += generate_type_cleanup_decl(expr['struct'] + "List")
+ fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n")
+ ret += generate_type_cleanup_decl(expr['struct'])
+ fdef.write(generate_type_cleanup(expr['struct']) + "\n")
elif expr.has_key('union'):
- ret += generate_union(expr)
+ ret += generate_union(expr, 'union')
ret += generate_type_cleanup_decl(expr['union'] + "List")
fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n")
ret += generate_type_cleanup_decl(expr['union'])
fdef.write(generate_type_cleanup(expr['union']) + "\n")
+ elif expr.has_key('alternate'):
+ ret += generate_union(expr, 'alternate')
+ ret += generate_type_cleanup_decl(expr['alternate'] + "List")
+ fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n")
+ ret += generate_type_cleanup_decl(expr['alternate'])
+ fdef.write(generate_type_cleanup(expr['alternate']) + "\n")
elif expr.has_key('enum'):
ret += generate_type_cleanup_decl(expr['enum'] + "List")
fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n")
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 1be4d67d8a..0e67b336fc 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -43,79 +43,45 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
''',
c_type=type_name(type))
-def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
+def generate_visit_struct_fields(name, members, base = None):
substructs = []
ret = ''
- if not fn_prefix:
- full_name = name
- else:
- full_name = "%s_%s" % (name, fn_prefix)
-
- for argname, argentry, optional, structured in parse_args(members):
- if structured:
- if not fn_prefix:
- nested_fn_prefix = argname
- else:
- nested_fn_prefix = "%s_%s" % (fn_prefix, argname)
-
- nested_field_prefix = "%s%s." % (field_prefix, argname)
- ret += generate_visit_struct_fields(name, nested_field_prefix,
- nested_fn_prefix, argentry)
- ret += mcgen('''
-
-static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp)
-{
-''',
- name=name, full_name=full_name, c_name=c_var(argname))
- ret += generate_visit_struct_body(full_name, argname, argentry)
- ret += mcgen('''
-}
-''')
if base:
ret += generate_visit_implicit_struct(base)
ret += mcgen('''
-static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
+static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
{
Error *err = NULL;
''',
- name=name, full_name=full_name)
+ name=name)
push_indent()
if base:
ret += mcgen('''
-visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
+visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
if (err) {
goto out;
}
''',
- c_prefix=c_var(field_prefix),
type=type_name(base), c_name=c_var('base'))
- for argname, argentry, optional, structured in parse_args(members):
+ for argname, argentry, optional in parse_args(members):
if optional:
ret += mcgen('''
-visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
-if (!err && (*obj)->%(prefix)shas_%(c_name)s) {
+visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
+if (!err && (*obj)->has_%(c_name)s) {
''',
- c_prefix=c_var(field_prefix), prefix=field_prefix,
c_name=c_var(argname), name=argname)
push_indent()
- if structured:
- ret += mcgen('''
-visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err);
-''',
- full_name=full_name, c_name=c_var(argname))
- else:
- ret += mcgen('''
-visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
+ ret += mcgen('''
+visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
''',
- c_prefix=c_var(field_prefix), prefix=field_prefix,
- type=type_name(argentry), c_name=c_var(argname),
- name=argname)
+ type=type_name(argentry), c_name=c_var(argname),
+ name=argname)
if optional:
pop_indent()
@@ -141,29 +107,11 @@ out:
return ret
-def generate_visit_struct_body(field_prefix, name, members):
+def generate_visit_struct_body(name, members):
ret = mcgen('''
Error *err = NULL;
-''')
-
- if not field_prefix:
- full_name = name
- else:
- full_name = "%s_%s" % (field_prefix, name)
-
- if len(field_prefix):
- ret += mcgen('''
- visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
-''',
- name=name)
- else:
- ret += mcgen('''
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
-''',
- name=name)
-
- ret += mcgen('''
if (!err) {
if (*obj) {
visit_type_%(name)s_fields(m, obj, errp);
@@ -172,17 +120,17 @@ def generate_visit_struct_body(field_prefix, name, members):
}
error_propagate(errp, err);
''',
- name=full_name)
+ name=name)
return ret
def generate_visit_struct(expr):
- name = expr['type']
+ name = expr['struct']
members = expr['data']
base = expr.get('base')
- ret = generate_visit_struct_fields(name, "", "", members, base)
+ ret = generate_visit_struct_fields(name, members, base)
ret += mcgen('''
@@ -191,7 +139,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
''',
name=name)
- ret += generate_visit_struct_body("", name, members)
+ ret += generate_visit_struct_body(name, members)
ret += mcgen('''
}
@@ -237,7 +185,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **er
''',
name=name)
-def generate_visit_anon_union(name, members):
+def generate_visit_alternate(name, members):
ret = mcgen('''
void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
@@ -256,15 +204,15 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
''',
name=name)
- # For anon union, always use the default enum type automatically generated
+ # For alternate, always use the default enum type automatically generated
# as "'%sKind' % (name)"
disc_type = '%sKind' % (name)
for key in members:
- assert (members[key] in builtin_types
+ assert (members[key] in builtin_types.keys()
or find_struct(members[key])
or find_union(members[key])
- or find_enum(members[key])), "Invalid anonymous union member"
+ or find_enum(members[key])), "Invalid alternate member"
enum_full_value = generate_enum_full_value(disc_type, key)
ret += mcgen('''
@@ -300,27 +248,22 @@ def generate_visit_union(expr):
base = expr.get('base')
discriminator = expr.get('discriminator')
- if discriminator == {}:
- assert not base
- return generate_visit_anon_union(name, members)
-
enum_define = discriminator_find_enum_define(expr)
if enum_define:
# Use the enum type as discriminator
ret = ""
disc_type = enum_define['enum_name']
else:
- # There will always be a discriminator in the C switch code, by default it
- # is an enum type generated silently as "'%sKind' % (name)"
+ # There will always be a discriminator in the C switch code, by default
+ # it is an enum type generated silently as "'%sKind' % (name)"
ret = generate_visit_enum('%sKind' % name, members.keys())
disc_type = '%sKind' % (name)
if base:
- base_fields = find_struct(base)['data']
- if discriminator:
- base_fields = base_fields.copy()
- del base_fields[discriminator]
- ret += generate_visit_struct_fields(name, "", "", base_fields)
+ assert discriminator
+ base_fields = find_struct(base)['data'].copy()
+ del base_fields[discriminator]
+ ret += generate_visit_struct_fields(name, base_fields)
if discriminator:
for key in members:
@@ -538,7 +481,7 @@ exprs = parse_schema(input_file)
# to avoid header dependency hell, we always generate declarations
# for built-in types in our header files and simply guard them
fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
-for typename in builtin_types:
+for typename in builtin_types.keys():
fdecl.write(generate_declaration(typename, None, builtin_type=True))
fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
@@ -546,16 +489,16 @@ fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
# have the functions defined, so we use -b option to provide control
# over these cases
if do_builtins:
- for typename in builtin_types:
+ for typename in builtin_types.keys():
fdef.write(generate_visit_list(typename, None))
for expr in exprs:
- if expr.has_key('type'):
+ if expr.has_key('struct'):
ret = generate_visit_struct(expr)
- ret += generate_visit_list(expr['type'], expr['data'])
+ ret += generate_visit_list(expr['struct'], expr['data'])
fdef.write(ret)
- ret = generate_declaration(expr['type'], expr['data'])
+ ret = generate_declaration(expr['struct'], expr['data'])
fdecl.write(ret)
elif expr.has_key('union'):
ret = generate_visit_union(expr)
@@ -569,6 +512,15 @@ for expr in exprs:
expr['data'].keys())
ret += generate_declaration(expr['union'], expr['data'])
fdecl.write(ret)
+ elif expr.has_key('alternate'):
+ ret = generate_visit_alternate(expr['alternate'], expr['data'])
+ ret += generate_visit_list(expr['alternate'], expr['data'])
+ fdef.write(ret)
+
+ ret = generate_decl_enum('%sKind' % expr['alternate'],
+ expr['data'].keys())
+ ret += generate_declaration(expr['alternate'], expr['data'])
+ fdecl.write(ret)
elif expr.has_key('enum'):
ret = generate_visit_list(expr['enum'], expr['data'])
ret += generate_visit_enum(expr['enum'], expr['data'])
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 77d46aa995..166b74f644 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -2,7 +2,7 @@
# QAPI helper library
#
# Copyright IBM, Corp. 2011
-# Copyright (c) 2013 Red Hat Inc.
+# Copyright (c) 2013-2015 Red Hat Inc.
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
@@ -16,13 +16,7 @@ from ordereddict import OrderedDict
import os
import sys
-builtin_types = [
- 'str', 'int', 'number', 'bool',
- 'int8', 'int16', 'int32', 'int64',
- 'uint8', 'uint16', 'uint32', 'uint64'
-]
-
-builtin_type_qtypes = {
+builtin_types = {
'str': 'QTYPE_QSTRING',
'int': 'QTYPE_QINT',
'number': 'QTYPE_QFLOAT',
@@ -35,8 +29,39 @@ builtin_type_qtypes = {
'uint16': 'QTYPE_QINT',
'uint32': 'QTYPE_QINT',
'uint64': 'QTYPE_QINT',
+ 'size': 'QTYPE_QINT',
}
+# Whitelist of commands allowed to return a non-dictionary
+returns_whitelist = [
+ # From QMP:
+ 'human-monitor-command',
+ 'query-migrate-cache-size',
+ 'query-tpm-models',
+ 'query-tpm-types',
+ 'ringbuf-read',
+
+ # From QGA:
+ 'guest-file-open',
+ 'guest-fsfreeze-freeze',
+ 'guest-fsfreeze-freeze-list',
+ 'guest-fsfreeze-status',
+ 'guest-fsfreeze-thaw',
+ 'guest-get-time',
+ 'guest-set-vcpus',
+ 'guest-sync',
+ 'guest-sync-delimited',
+
+ # From qapi-schema-test:
+ 'user_def_cmd3',
+]
+
+enum_types = []
+struct_types = []
+union_types = []
+events = []
+all_names = {}
+
def error_path(parent):
res = ""
while parent:
@@ -148,7 +173,41 @@ class QAPISchema:
raise QAPISchemaError(self,
'Missing terminating "\'"')
if esc:
- string += ch
+ if ch == 'b':
+ string += '\b'
+ elif ch == 'f':
+ string += '\f'
+ elif ch == 'n':
+ string += '\n'
+ elif ch == 'r':
+ string += '\r'
+ elif ch == 't':
+ string += '\t'
+ elif ch == 'u':
+ value = 0
+ for x in range(0, 4):
+ ch = self.src[self.cursor]
+ self.cursor += 1
+ if ch not in "0123456789abcdefABCDEF":
+ raise QAPISchemaError(self,
+ '\\u escape needs 4 '
+ 'hex digits')
+ value = (value << 4) + int(ch, 16)
+ # If Python 2 and 3 didn't disagree so much on
+ # how to handle Unicode, then we could allow
+ # Unicode string defaults. But most of QAPI is
+ # ASCII-only, so we aren't losing much for now.
+ if not value or value > 0x7f:
+ raise QAPISchemaError(self,
+ 'For now, \\u escape '
+ 'only supports non-zero '
+ 'values up to \\u007f')
+ string += chr(value)
+ elif ch in "\\/'\"":
+ string += ch
+ else:
+ raise QAPISchemaError(self,
+ "Unknown escape \\%s" %ch)
esc = False
elif ch == "\\":
esc = True
@@ -157,6 +216,20 @@ class QAPISchema:
return
else:
string += ch
+ elif self.tok in "tfn":
+ val = self.src[self.cursor - 1:]
+ if val.startswith("true"):
+ self.val = True
+ self.cursor += 3
+ return
+ elif val.startswith("false"):
+ self.val = False
+ self.cursor += 4
+ return
+ elif val.startswith("null"):
+ self.val = None
+ self.cursor += 3
+ return
elif self.tok == '\n':
if self.cursor == len(self.src):
self.tok = None
@@ -196,8 +269,9 @@ class QAPISchema:
if self.tok == ']':
self.accept()
return expr
- if not self.tok in [ '{', '[', "'" ]:
- raise QAPISchemaError(self, 'Expected "{", "[", "]" or string')
+ if not self.tok in "{['tfn":
+ raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
+ 'boolean or "null"')
while True:
expr.append(self.get_expr(True))
if self.tok == ']':
@@ -216,7 +290,7 @@ class QAPISchema:
elif self.tok == '[':
self.accept()
expr = self.get_values()
- elif self.tok == "'":
+ elif self.tok in "'tfn":
expr = self.val
self.accept()
else:
@@ -229,6 +303,18 @@ def find_base_fields(base):
return None
return base_struct_define['data']
+# Return the qtype of an alternate branch, or None on error.
+def find_alternate_member_qtype(qapi_type):
+ if builtin_types.has_key(qapi_type):
+ return builtin_types[qapi_type]
+ elif find_struct(qapi_type):
+ return "QTYPE_QDICT"
+ elif find_enum(qapi_type):
+ return "QTYPE_QSTRING"
+ elif find_union(qapi_type):
+ return "QTYPE_QDICT"
+ return None
+
# Return the discriminator enum define if discriminator is specified as an
# enum type, otherwise return None.
def discriminator_find_enum_define(expr):
@@ -248,56 +334,178 @@ def discriminator_find_enum_define(expr):
return find_enum(discriminator_type)
+valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$')
+def check_name(expr_info, source, name, allow_optional = False,
+ enum_member = False):
+ global valid_name
+ membername = name
+
+ if not isinstance(name, str):
+ raise QAPIExprError(expr_info,
+ "%s requires a string name" % source)
+ if name.startswith('*'):
+ membername = name[1:]
+ if not allow_optional:
+ raise QAPIExprError(expr_info,
+ "%s does not allow optional name '%s'"
+ % (source, name))
+ # Enum members can start with a digit, because the generated C
+ # code always prefixes it with the enum name
+ if enum_member:
+ membername = '_' + membername
+ if not valid_name.match(membername):
+ raise QAPIExprError(expr_info,
+ "%s uses invalid name '%s'" % (source, name))
+
+def check_type(expr_info, source, value, allow_array = False,
+ allow_dict = False, allow_optional = False,
+ allow_star = False, allow_metas = []):
+ global all_names
+ orig_value = value
+
+ if value is None:
+ return
+
+ if allow_star and value == '**':
+ return
+
+ # Check if array type for value is okay
+ if isinstance(value, list):
+ if not allow_array:
+ raise QAPIExprError(expr_info,
+ "%s cannot be an array" % source)
+ if len(value) != 1 or not isinstance(value[0], str):
+ raise QAPIExprError(expr_info,
+ "%s: array type must contain single type name"
+ % source)
+ value = value[0]
+ orig_value = "array of %s" %value
+
+ # Check if type name for value is okay
+ if isinstance(value, str):
+ if value == '**':
+ raise QAPIExprError(expr_info,
+ "%s uses '**' but did not request 'gen':false"
+ % source)
+ if not value in all_names:
+ raise QAPIExprError(expr_info,
+ "%s uses unknown type '%s'"
+ % (source, orig_value))
+ if not all_names[value] in allow_metas:
+ raise QAPIExprError(expr_info,
+ "%s cannot use %s type '%s'"
+ % (source, all_names[value], orig_value))
+ return
+
+ # value is a dictionary, check that each member is okay
+ if not isinstance(value, OrderedDict):
+ raise QAPIExprError(expr_info,
+ "%s should be a dictionary" % source)
+ if not allow_dict:
+ raise QAPIExprError(expr_info,
+ "%s should be a type name" % source)
+ for (key, arg) in value.items():
+ check_name(expr_info, "Member of %s" % source, key,
+ allow_optional=allow_optional)
+ # Todo: allow dictionaries to represent default values of
+ # an optional argument.
+ check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
+ allow_array=True, allow_star=allow_star,
+ allow_metas=['built-in', 'union', 'alternate', 'struct',
+ 'enum'])
+
+def check_member_clash(expr_info, base_name, data, source = ""):
+ base = find_struct(base_name)
+ assert base
+ base_members = base['data']
+ for key in data.keys():
+ if key.startswith('*'):
+ key = key[1:]
+ if key in base_members or "*" + key in base_members:
+ raise QAPIExprError(expr_info,
+ "Member name '%s'%s clashes with base '%s'"
+ % (key, source, base_name))
+ if base.get('base'):
+ check_member_clash(expr_info, base['base'], data, source)
+
+def check_command(expr, expr_info):
+ name = expr['command']
+ allow_star = expr.has_key('gen')
+
+ check_type(expr_info, "'data' for command '%s'" % name,
+ expr.get('data'), allow_dict=True, allow_optional=True,
+ allow_metas=['union', 'struct'], allow_star=allow_star)
+ returns_meta = ['union', 'struct']
+ if name in returns_whitelist:
+ returns_meta += ['built-in', 'alternate', 'enum']
+ check_type(expr_info, "'returns' for command '%s'" % name,
+ expr.get('returns'), allow_array=True, allow_dict=True,
+ allow_optional=True, allow_metas=returns_meta,
+ allow_star=allow_star)
+
def check_event(expr, expr_info):
+ global events
+ name = expr['event']
params = expr.get('data')
- if params:
- for argname, argentry, optional, structured in parse_args(params):
- if structured:
- raise QAPIExprError(expr_info,
- "Nested structure define in event is not "
- "supported, event '%s', argname '%s'"
- % (expr['event'], argname))
+
+ if name.upper() == 'MAX':
+ raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created")
+ events.append(name)
+ check_type(expr_info, "'data' for event '%s'" % name,
+ expr.get('data'), allow_dict=True, allow_optional=True,
+ allow_metas=['union', 'struct'])
def check_union(expr, expr_info):
name = expr['union']
base = expr.get('base')
discriminator = expr.get('discriminator')
members = expr['data']
+ values = { 'MAX': '(automatic)' }
- # If the object has a member 'base', its value must name a complex type.
- if base:
- base_fields = find_base_fields(base)
- if not base_fields:
+ # If the object has a member 'base', its value must name a struct,
+ # and there must be a discriminator.
+ if base is not None:
+ if discriminator is None:
raise QAPIExprError(expr_info,
- "Base '%s' is not a valid type"
- % base)
+ "Union '%s' requires a discriminator to go "
+ "along with base" %name)
- # If the union object has no member 'discriminator', it's an
- # ordinary union.
- if not discriminator:
- enum_define = None
+ # Two types of unions, determined by discriminator.
- # Else if the value of member 'discriminator' is {}, it's an
- # anonymous union.
- elif discriminator == {}:
+ # With no discriminator it is a simple union.
+ if discriminator is None:
enum_define = None
+ allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']
+ if base is not None:
+ raise QAPIExprError(expr_info,
+ "Simple union '%s' must not have a base"
+ % name)
# Else, it's a flat union.
else:
- # The object must have a member 'base'.
- if not base:
+ # The object must have a string member 'base'.
+ if not isinstance(base, str):
raise QAPIExprError(expr_info,
- "Flat union '%s' must have a base field"
+ "Flat union '%s' must have a string base field"
% name)
- # The value of member 'discriminator' must name a member of the
- # base type.
+ base_fields = find_base_fields(base)
+ if not base_fields:
+ raise QAPIExprError(expr_info,
+ "Base '%s' is not a valid struct"
+ % base)
+
+ # The value of member 'discriminator' must name a non-optional
+ # member of the base struct.
+ check_name(expr_info, "Discriminator of flat union '%s'" % name,
+ discriminator)
discriminator_type = base_fields.get(discriminator)
if not discriminator_type:
raise QAPIExprError(expr_info,
"Discriminator '%s' is not a member of base "
- "type '%s'"
+ "struct '%s'"
% (discriminator, base))
enum_define = find_enum(discriminator_type)
+ allow_metas=['struct']
# Do not allow string discriminator
if not enum_define:
raise QAPIExprError(expr_info,
@@ -306,51 +514,196 @@ def check_union(expr, expr_info):
# Check every branch
for (key, value) in members.items():
- # If this named member's value names an enum type, then all members
+ check_name(expr_info, "Member of union '%s'" % name, key)
+
+ # Each value must name a known type; furthermore, in flat unions,
+ # branches must be a struct with no overlapping member names
+ check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
+ value, allow_array=True, allow_metas=allow_metas)
+ if base:
+ branch_struct = find_struct(value)
+ assert branch_struct
+ check_member_clash(expr_info, base, branch_struct['data'],
+ " of branch '%s'" % key)
+
+ # If the discriminator names an enum type, then all members
# of 'data' must also be members of the enum type.
- if enum_define and not key in enum_define['enum_values']:
+ if enum_define:
+ if not key in enum_define['enum_values']:
+ raise QAPIExprError(expr_info,
+ "Discriminator value '%s' is not found in "
+ "enum '%s'" %
+ (key, enum_define["enum_name"]))
+
+ # Otherwise, check for conflicts in the generated enum
+ else:
+ c_key = _generate_enum_string(key)
+ if c_key in values:
+ raise QAPIExprError(expr_info,
+ "Union '%s' member '%s' clashes with '%s'"
+ % (name, key, values[c_key]))
+ values[c_key] = key
+
+def check_alternate(expr, expr_info):
+ name = expr['alternate']
+ members = expr['data']
+ values = { 'MAX': '(automatic)' }
+ types_seen = {}
+
+ # Check every branch
+ for (key, value) in members.items():
+ check_name(expr_info, "Member of alternate '%s'" % name, key)
+
+ # Check for conflicts in the generated enum
+ c_key = _generate_enum_string(key)
+ if c_key in values:
raise QAPIExprError(expr_info,
- "Discriminator value '%s' is not found in "
- "enum '%s'" %
- (key, enum_define["enum_name"]))
- # Todo: add checking for values. Key is checked as above, value can be
- # also checked here, but we need more functions to handle array case.
+ "Alternate '%s' member '%s' clashes with '%s'"
+ % (name, key, values[c_key]))
+ values[c_key] = key
+
+ # Ensure alternates have no type conflicts.
+ check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
+ value,
+ allow_metas=['built-in', 'union', 'struct', 'enum'])
+ qtype = find_alternate_member_qtype(value)
+ assert qtype
+ if qtype in types_seen:
+ raise QAPIExprError(expr_info,
+ "Alternate '%s' member '%s' can't "
+ "be distinguished from member '%s'"
+ % (name, key, types_seen[qtype]))
+ types_seen[qtype] = key
+
+def check_enum(expr, expr_info):
+ name = expr['enum']
+ members = expr.get('data')
+ values = { 'MAX': '(automatic)' }
+
+ if not isinstance(members, list):
+ raise QAPIExprError(expr_info,
+ "Enum '%s' requires an array for 'data'" % name)
+ for member in members:
+ check_name(expr_info, "Member of enum '%s'" %name, member,
+ enum_member=True)
+ key = _generate_enum_string(member)
+ if key in values:
+ raise QAPIExprError(expr_info,
+ "Enum '%s' member '%s' clashes with '%s'"
+ % (name, member, values[key]))
+ values[key] = member
+
+def check_struct(expr, expr_info):
+ name = expr['struct']
+ members = expr['data']
+
+ check_type(expr_info, "'data' for struct '%s'" % name, members,
+ allow_dict=True, allow_optional=True)
+ check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
+ allow_metas=['struct'])
+ if expr.get('base'):
+ check_member_clash(expr_info, expr['base'], expr['data'])
def check_exprs(schema):
for expr_elem in schema.exprs:
expr = expr_elem['expr']
- if expr.has_key('union'):
- check_union(expr, expr_elem['info'])
- if expr.has_key('event'):
- check_event(expr, expr_elem['info'])
+ info = expr_elem['info']
+
+ if expr.has_key('enum'):
+ check_enum(expr, info)
+ elif expr.has_key('union'):
+ check_union(expr, info)
+ elif expr.has_key('alternate'):
+ check_alternate(expr, info)
+ elif expr.has_key('struct'):
+ check_struct(expr, info)
+ elif expr.has_key('command'):
+ check_command(expr, info)
+ elif expr.has_key('event'):
+ check_event(expr, info)
+ else:
+ assert False, 'unexpected meta type'
+
+def check_keys(expr_elem, meta, required, optional=[]):
+ expr = expr_elem['expr']
+ info = expr_elem['info']
+ name = expr[meta]
+ if not isinstance(name, str):
+ raise QAPIExprError(info,
+ "'%s' key must have a string value" % meta)
+ required = required + [ meta ]
+ for (key, value) in expr.items():
+ if not key in required and not key in optional:
+ raise QAPIExprError(info,
+ "Unknown key '%s' in %s '%s'"
+ % (key, meta, name))
+ if (key == 'gen' or key == 'success-response') and value != False:
+ raise QAPIExprError(info,
+ "'%s' of %s '%s' should only use false value"
+ % (key, meta, name))
+ for key in required:
+ if not expr.has_key(key):
+ raise QAPIExprError(info,
+ "Key '%s' is missing from %s '%s'"
+ % (key, meta, name))
+
def parse_schema(input_file):
+ global all_names
+ exprs = []
+
+ # First pass: read entire file into memory
try:
schema = QAPISchema(open(input_file, "r"))
except (QAPISchemaError, QAPIExprError), e:
print >>sys.stderr, e
exit(1)
- exprs = []
-
- for expr_elem in schema.exprs:
- expr = expr_elem['expr']
- if expr.has_key('enum'):
- add_enum(expr['enum'], expr['data'])
- elif expr.has_key('union'):
- add_union(expr)
- elif expr.has_key('type'):
- add_struct(expr)
- exprs.append(expr)
-
- # Try again for hidden UnionKind enum
- for expr_elem in schema.exprs:
- expr = expr_elem['expr']
- if expr.has_key('union'):
- if not discriminator_find_enum_define(expr):
- add_enum('%sKind' % expr['union'])
-
try:
+ # Next pass: learn the types and check for valid expression keys. At
+ # this point, top-level 'include' has already been flattened.
+ for builtin in builtin_types.keys():
+ all_names[builtin] = 'built-in'
+ for expr_elem in schema.exprs:
+ expr = expr_elem['expr']
+ info = expr_elem['info']
+ if expr.has_key('enum'):
+ check_keys(expr_elem, 'enum', ['data'])
+ add_enum(expr['enum'], info, expr['data'])
+ elif expr.has_key('union'):
+ check_keys(expr_elem, 'union', ['data'],
+ ['base', 'discriminator'])
+ add_union(expr, info)
+ elif expr.has_key('alternate'):
+ check_keys(expr_elem, 'alternate', ['data'])
+ add_name(expr['alternate'], info, 'alternate')
+ elif expr.has_key('struct'):
+ check_keys(expr_elem, 'struct', ['data'], ['base'])
+ add_struct(expr, info)
+ elif expr.has_key('command'):
+ check_keys(expr_elem, 'command', [],
+ ['data', 'returns', 'gen', 'success-response'])
+ add_name(expr['command'], info, 'command')
+ elif expr.has_key('event'):
+ check_keys(expr_elem, 'event', [], ['data'])
+ add_name(expr['event'], info, 'event')
+ else:
+ raise QAPIExprError(expr_elem['info'],
+ "Expression is missing metatype")
+ exprs.append(expr)
+
+ # Try again for hidden UnionKind enum
+ for expr_elem in schema.exprs:
+ expr = expr_elem['expr']
+ if expr.has_key('union'):
+ if not discriminator_find_enum_define(expr):
+ add_enum('%sKind' % expr['union'], expr_elem['info'],
+ implicit=True)
+ elif expr.has_key('alternate'):
+ add_enum('%sKind' % expr['alternate'], expr_elem['info'],
+ implicit=True)
+
+ # Final pass - validate that exprs make sense
check_exprs(schema)
except QAPIExprError, e:
print >>sys.stderr, e
@@ -359,7 +712,7 @@ def parse_schema(input_file):
return exprs
def parse_args(typeinfo):
- if isinstance(typeinfo, basestring):
+ if isinstance(typeinfo, str):
struct = find_struct(typeinfo)
assert struct != None
typeinfo = struct['data']
@@ -368,13 +721,12 @@ def parse_args(typeinfo):
argname = member
argentry = typeinfo[member]
optional = False
- structured = False
if member.startswith('*'):
argname = member[1:]
optional = True
- if isinstance(argentry, OrderedDict):
- structured = True
- yield (argname, argentry, optional, structured)
+ # Todo: allow argentry to be OrderedDict, for providing the
+ # value of an optional argument.
+ yield (argname, argentry, optional)
def de_camel_case(name):
new_name = ''
@@ -442,23 +794,36 @@ def type_name(name):
return c_list_type(name[0])
return name
-enum_types = []
-struct_types = []
-union_types = []
-
-def add_struct(definition):
+def add_name(name, info, meta, implicit = False):
+ global all_names
+ check_name(info, "'%s'" % meta, name)
+ if name in all_names:
+ raise QAPIExprError(info,
+ "%s '%s' is already defined"
+ % (all_names[name], name))
+ if not implicit and name[-4:] == 'Kind':
+ raise QAPIExprError(info,
+ "%s '%s' should not end in 'Kind'"
+ % (meta, name))
+ all_names[name] = meta
+
+def add_struct(definition, info):
global struct_types
+ name = definition['struct']
+ add_name(name, info, 'struct')
struct_types.append(definition)
def find_struct(name):
global struct_types
for struct in struct_types:
- if struct['type'] == name:
+ if struct['struct'] == name:
return struct
return None
-def add_union(definition):
+def add_union(definition, info):
global union_types
+ name = definition['union']
+ add_name(name, info, 'union')
union_types.append(definition)
def find_union(name):
@@ -468,8 +833,9 @@ def find_union(name):
return union
return None
-def add_enum(name, enum_values = None):
+def add_enum(name, info, enum_values = None, implicit = False):
global enum_types
+ add_name(name, info, 'enum', implicit)
enum_types.append({"enum_name": name, "enum_values": enum_values})
def find_enum(name):
@@ -511,7 +877,7 @@ def c_type(name, is_param=False):
return name
elif name == None or len(name) == 0:
return 'void'
- elif name == name.upper():
+ elif name in events:
return '%sEvent *%s' % (camel_case(name), eatspace)
else:
return '%s *%s' % (name, eatspace)
diff --git a/tests/Makefile b/tests/Makefile
index 309e8697fd..666aee2ac3 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -207,20 +207,44 @@ $(foreach target,$(SYSEMU_TARGET_LIST), \
$(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF))))
check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
- comments.json empty.json funny-char.json indented-expr.json \
- missing-colon.json missing-comma-list.json \
- missing-comma-object.json non-objects.json \
+ comments.json empty.json enum-empty.json enum-missing-data.json \
+ enum-wrong-data.json enum-int-member.json enum-dict-member.json \
+ enum-clash-member.json enum-max-member.json enum-union-clash.json \
+ enum-bad-name.json funny-char.json indented-expr.json \
+ missing-type.json bad-ident.json ident-with-escape.json \
+ escape-outside-string.json unknown-escape.json \
+ escape-too-short.json escape-too-big.json unicode-str.json \
+ double-type.json bad-base.json bad-type-bool.json bad-type-int.json \
+ bad-type-dict.json double-data.json unknown-expr-key.json \
+ redefined-type.json redefined-command.json redefined-builtin.json \
+ redefined-event.json command-int.json bad-data.json event-max.json \
+ type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
+ data-array-empty.json data-array-unknown.json data-int.json \
+ data-unknown.json data-member-unknown.json data-member-array.json \
+ data-member-array-bad.json returns-array-bad.json returns-int.json \
+ returns-unknown.json returns-alternate.json returns-whitelist.json \
+ missing-colon.json missing-comma-list.json missing-comma-object.json \
+ nested-struct-data.json nested-struct-returns.json non-objects.json \
qapi-schema-test.json quoted-structural-chars.json \
trailing-comma-list.json trailing-comma-object.json \
unclosed-list.json unclosed-object.json unclosed-string.json \
- duplicate-key.json union-invalid-base.json flat-union-no-base.json \
- flat-union-invalid-discriminator.json \
+ duplicate-key.json union-invalid-base.json union-bad-branch.json \
+ union-optional-branch.json union-unknown.json union-max.json \
+ flat-union-optional-discriminator.json flat-union-no-base.json \
+ flat-union-invalid-discriminator.json flat-union-inline.json \
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
- flat-union-string-discriminator.json \
+ flat-union-string-discriminator.json union-base-no-discriminator.json \
+ flat-union-bad-discriminator.json flat-union-bad-base.json \
+ flat-union-base-star.json flat-union-int-branch.json \
+ flat-union-base-union.json flat-union-branch-clash.json \
+ alternate-nested.json alternate-unknown.json alternate-clash.json \
+ alternate-good.json alternate-base.json alternate-array.json \
+ alternate-conflict-string.json alternate-conflict-dict.json \
include-simple.json include-relpath.json include-format-err.json \
include-non-file.json include-no-file.json include-before-err.json \
include-nested-err.json include-self-cycle.json include-cycle.json \
- include-repetition.json event-nest-struct.json)
+ include-repetition.json event-nest-struct.json event-case.json \
+ struct-base-clash.json struct-base-clash-deep.json )
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
tests/test-qmp-commands.h tests/test-qapi-event.h
diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err
new file mode 100644
index 0000000000..7b930c64ab
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
diff --git a/tests/qapi-schema/alternate-array.exit b/tests/qapi-schema/alternate-array.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-array.json b/tests/qapi-schema/alternate-array.json
new file mode 100644
index 0000000000..f241aac122
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.json
@@ -0,0 +1,7 @@
+# we do not allow array branches in alternates
+# TODO: should we support this?
+{ 'struct': 'One',
+ 'data': { 'name': 'str' } }
+{ 'alternate': 'Alt',
+ 'data': { 'one': 'One',
+ 'two': [ 'int' ] } }
diff --git a/tests/qapi-schema/alternate-array.out b/tests/qapi-schema/alternate-array.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.out
diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err
new file mode 100644
index 0000000000..30d8a34373
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
diff --git a/tests/qapi-schema/alternate-base.exit b/tests/qapi-schema/alternate-base.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-base.json b/tests/qapi-schema/alternate-base.json
new file mode 100644
index 0000000000..529430ecf2
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.json
@@ -0,0 +1,6 @@
+# we reject alternate with base type
+{ 'struct': 'Base',
+ 'data': { 'string': 'str' } }
+{ 'alternate': 'Alt',
+ 'base': 'Base',
+ 'data': { 'number': 'int' } }
diff --git a/tests/qapi-schema/alternate-base.out b/tests/qapi-schema/alternate-base.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.out
diff --git a/tests/qapi-schema/alternate-clash.err b/tests/qapi-schema/alternate-clash.err
new file mode 100644
index 0000000000..51bea3e272
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-clash.json:2: Alternate 'Alt1' member 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/alternate-clash.exit b/tests/qapi-schema/alternate-clash.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-clash.json b/tests/qapi-schema/alternate-clash.json
new file mode 100644
index 0000000000..39479353bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.json
@@ -0,0 +1,3 @@
+# we detect C enum collisions in an alternate
+{ 'alternate': 'Alt1',
+ 'data': { 'one': 'str', 'ONE': 'int' } }
diff --git a/tests/qapi-schema/alternate-clash.out b/tests/qapi-schema/alternate-clash.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.out
diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
new file mode 100644
index 0000000000..0f411f4faf
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-conflict-dict.json:6: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-dict.exit b/tests/qapi-schema/alternate-conflict-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-conflict-dict.json b/tests/qapi-schema/alternate-conflict-dict.json
new file mode 100644
index 0000000000..d566cca816
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.json
@@ -0,0 +1,8 @@
+# we reject alternates with multiple object branches
+{ 'struct': 'One',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Two',
+ 'data': { 'value': 'int' } }
+{ 'alternate': 'Alt',
+ 'data': { 'one': 'One',
+ 'two': 'Two' } }
diff --git a/tests/qapi-schema/alternate-conflict-dict.out b/tests/qapi-schema/alternate-conflict-dict.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.out
diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
new file mode 100644
index 0000000000..fc523b0879
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-conflict-string.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-string.exit b/tests/qapi-schema/alternate-conflict-string.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-conflict-string.json b/tests/qapi-schema/alternate-conflict-string.json
new file mode 100644
index 0000000000..72f04a820a
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.json
@@ -0,0 +1,6 @@
+# we reject alternates with multiple string-like branches
+{ 'enum': 'Enum',
+ 'data': [ 'hello', 'world' ] }
+{ 'alternate': 'Alt',
+ 'data': { 'one': 'str',
+ 'two': 'Enum' } }
diff --git a/tests/qapi-schema/alternate-conflict-string.out b/tests/qapi-schema/alternate-conflict-string.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.out
diff --git a/tests/qapi-schema/alternate-good.err b/tests/qapi-schema/alternate-good.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.err
diff --git a/tests/qapi-schema/alternate-good.exit b/tests/qapi-schema/alternate-good.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/alternate-good.json b/tests/qapi-schema/alternate-good.json
new file mode 100644
index 0000000000..33717704ce
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.json
@@ -0,0 +1,9 @@
+# Working example of alternate
+{ 'struct': 'Data',
+ 'data': { '*number': 'int', '*name': 'str' } }
+{ 'enum': 'Enum',
+ 'data': [ 'hello', 'world' ] }
+{ 'alternate': 'Alt',
+ 'data': { 'value': 'int',
+ 'string': 'Enum',
+ 'struct': 'Data' } }
diff --git a/tests/qapi-schema/alternate-good.out b/tests/qapi-schema/alternate-good.out
new file mode 100644
index 0000000000..99848eefbb
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.out
@@ -0,0 +1,6 @@
+[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]),
+ OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
+ OrderedDict([('alternate', 'Alt'), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])]
+[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
+ {'enum_name': 'AltKind', 'enum_values': None}]
+[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])]
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
new file mode 100644
index 0000000000..4d1187e60e
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-nested.json:4: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
diff --git a/tests/qapi-schema/alternate-nested.exit b/tests/qapi-schema/alternate-nested.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-nested.json b/tests/qapi-schema/alternate-nested.json
new file mode 100644
index 0000000000..c4233b9f33
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.json
@@ -0,0 +1,5 @@
+# we reject a nested alternate branch
+{ 'alternate': 'Alt1',
+ 'data': { 'name': 'str', 'value': 'int' } }
+{ 'alternate': 'Alt2',
+ 'data': { 'nested': 'Alt1' } }
diff --git a/tests/qapi-schema/alternate-nested.out b/tests/qapi-schema/alternate-nested.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.out
diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
new file mode 100644
index 0000000000..dea45dc730
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/alternate-unknown.exit b/tests/qapi-schema/alternate-unknown.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-unknown.json b/tests/qapi-schema/alternate-unknown.json
new file mode 100644
index 0000000000..ad5c103028
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.json
@@ -0,0 +1,3 @@
+# we reject an alternate with unknown type in branch
+{ 'alternate': 'Alt',
+ 'data': { 'unknown': 'MissingType' } }
diff --git a/tests/qapi-schema/alternate-unknown.out b/tests/qapi-schema/alternate-unknown.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.out
diff --git a/tests/qapi-schema/bad-base.err b/tests/qapi-schema/bad-base.err
new file mode 100644
index 0000000000..154274bdd3
--- /dev/null
+++ b/tests/qapi-schema/bad-base.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union'
diff --git a/tests/qapi-schema/bad-base.exit b/tests/qapi-schema/bad-base.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-base.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-base.json b/tests/qapi-schema/bad-base.json
new file mode 100644
index 0000000000..a634331cdd
--- /dev/null
+++ b/tests/qapi-schema/bad-base.json
@@ -0,0 +1,3 @@
+# we reject a base that is not a struct
+{ 'union': 'Union', 'data': { 'a': 'int', 'b': 'str' } }
+{ 'struct': 'MyType', 'base': 'Union', 'data': { 'c': 'int' } }
diff --git a/tests/qapi-schema/bad-base.out b/tests/qapi-schema/bad-base.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-base.out
diff --git a/tests/qapi-schema/bad-data.err b/tests/qapi-schema/bad-data.err
new file mode 100644
index 0000000000..8523ac4f46
--- /dev/null
+++ b/tests/qapi-schema/bad-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-data.json:2: 'data' for command 'oops' cannot be an array
diff --git a/tests/qapi-schema/bad-data.exit b/tests/qapi-schema/bad-data.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-data.json b/tests/qapi-schema/bad-data.json
new file mode 100644
index 0000000000..832eeb76f4
--- /dev/null
+++ b/tests/qapi-schema/bad-data.json
@@ -0,0 +1,2 @@
+# we ensure 'data' is a dictionary for all but enums
+{ 'command': 'oops', 'data': [ ] }
diff --git a/tests/qapi-schema/bad-data.out b/tests/qapi-schema/bad-data.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-data.out
diff --git a/tests/qapi-schema/bad-ident.err b/tests/qapi-schema/bad-ident.err
new file mode 100644
index 0000000000..c4190602b5
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-ident.json:2: 'struct' does not allow optional name '*oops'
diff --git a/tests/qapi-schema/bad-ident.exit b/tests/qapi-schema/bad-ident.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-ident.json b/tests/qapi-schema/bad-ident.json
new file mode 100644
index 0000000000..763627ad23
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.json
@@ -0,0 +1,2 @@
+# we reject creating a type name with bad name
+{ 'struct': '*oops', 'data': { 'i': 'int' } }
diff --git a/tests/qapi-schema/bad-ident.out b/tests/qapi-schema/bad-ident.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.out
diff --git a/tests/qapi-schema/bad-type-bool.err b/tests/qapi-schema/bad-type-bool.err
new file mode 100644
index 0000000000..62fd70baaf
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-type-bool.json:2: 'struct' key must have a string value
diff --git a/tests/qapi-schema/bad-type-bool.exit b/tests/qapi-schema/bad-type-bool.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-type-bool.json b/tests/qapi-schema/bad-type-bool.json
new file mode 100644
index 0000000000..bde17b56c4
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.json
@@ -0,0 +1,2 @@
+# we reject an expression with a metatype that is not a string
+{ 'struct': true, 'data': { } }
diff --git a/tests/qapi-schema/bad-type-bool.out b/tests/qapi-schema/bad-type-bool.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.out
diff --git a/tests/qapi-schema/bad-type-dict.err b/tests/qapi-schema/bad-type-dict.err
new file mode 100644
index 0000000000..0b2a2aeac4
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-type-dict.json:2: 'command' key must have a string value
diff --git a/tests/qapi-schema/bad-type-dict.exit b/tests/qapi-schema/bad-type-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-type-dict.json b/tests/qapi-schema/bad-type-dict.json
new file mode 100644
index 0000000000..2a91b241f8
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.json
@@ -0,0 +1,2 @@
+# we reject an expression with a metatype that is not a string
+{ 'command': { } }
diff --git a/tests/qapi-schema/bad-type-dict.out b/tests/qapi-schema/bad-type-dict.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.out
diff --git a/tests/qapi-schema/bad-type-int.err b/tests/qapi-schema/bad-type-int.err
new file mode 100644
index 0000000000..da89895404
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-type-int.json:3:13: Stray "1"
diff --git a/tests/qapi-schema/bad-type-int.exit b/tests/qapi-schema/bad-type-int.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-type-int.json b/tests/qapi-schema/bad-type-int.json
new file mode 100644
index 0000000000..56fc6f8126
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.json
@@ -0,0 +1,3 @@
+# we reject an expression with a metatype that is not a string
+# FIXME: once the parser understands integer inputs, improve the error message
+{ 'struct': 1, 'data': { } }
diff --git a/tests/qapi-schema/bad-type-int.out b/tests/qapi-schema/bad-type-int.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.out
diff --git a/tests/qapi-schema/command-int.err b/tests/qapi-schema/command-int.err
new file mode 100644
index 0000000000..0f9300679b
--- /dev/null
+++ b/tests/qapi-schema/command-int.err
@@ -0,0 +1 @@
+tests/qapi-schema/command-int.json:2: built-in 'int' is already defined
diff --git a/tests/qapi-schema/command-int.exit b/tests/qapi-schema/command-int.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/command-int.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/command-int.json b/tests/qapi-schema/command-int.json
new file mode 100644
index 0000000000..c90d408abe
--- /dev/null
+++ b/tests/qapi-schema/command-int.json
@@ -0,0 +1,3 @@
+# we reject collisions between commands and types
+{ 'command': 'int', 'data': { 'character': 'str' },
+ 'returns': { 'value': 'int' } }
diff --git a/tests/qapi-schema/command-int.out b/tests/qapi-schema/command-int.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/command-int.out
diff --git a/tests/qapi-schema/data-array-empty.err b/tests/qapi-schema/data-array-empty.err
new file mode 100644
index 0000000000..f713f14893
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/data-array-empty.exit b/tests/qapi-schema/data-array-empty.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-array-empty.json b/tests/qapi-schema/data-array-empty.json
new file mode 100644
index 0000000000..652dcfb24a
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.json
@@ -0,0 +1,2 @@
+# we reject an array for data if it does not contain a known type
+{ 'command': 'oops', 'data': { 'empty': [ ] } }
diff --git a/tests/qapi-schema/data-array-empty.out b/tests/qapi-schema/data-array-empty.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.out
diff --git a/tests/qapi-schema/data-array-unknown.err b/tests/qapi-schema/data-array-unknown.err
new file mode 100644
index 0000000000..8b731bbcc8
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'array of NoSuchType'
diff --git a/tests/qapi-schema/data-array-unknown.exit b/tests/qapi-schema/data-array-unknown.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-array-unknown.json b/tests/qapi-schema/data-array-unknown.json
new file mode 100644
index 0000000000..6f3e883315
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.json
@@ -0,0 +1,2 @@
+# we reject an array for data if it does not contain a known type
+{ 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }
diff --git a/tests/qapi-schema/data-array-unknown.out b/tests/qapi-schema/data-array-unknown.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.out
diff --git a/tests/qapi-schema/data-int.err b/tests/qapi-schema/data-int.err
new file mode 100644
index 0000000000..1a9b077c06
--- /dev/null
+++ b/tests/qapi-schema/data-int.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/data-int.exit b/tests/qapi-schema/data-int.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/data-int.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-int.json b/tests/qapi-schema/data-int.json
new file mode 100644
index 0000000000..a334d92e8c
--- /dev/null
+++ b/tests/qapi-schema/data-int.json
@@ -0,0 +1,2 @@
+# we reject commands where data is not an array or complex type
+{ 'command': 'oops', 'data': 'int' }
diff --git a/tests/qapi-schema/data-int.out b/tests/qapi-schema/data-int.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/data-int.out
diff --git a/tests/qapi-schema/data-member-array-bad.err b/tests/qapi-schema/data-member-array-bad.err
new file mode 100644
index 0000000000..2c072d5986
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/data-member-array-bad.exit b/tests/qapi-schema/data-member-array-bad.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-member-array-bad.json b/tests/qapi-schema/data-member-array-bad.json
new file mode 100644
index 0000000000..b2ff144ec6
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.json
@@ -0,0 +1,2 @@
+# we reject data if it does not contain a valid array type
+{ 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }
diff --git a/tests/qapi-schema/data-member-array-bad.out b/tests/qapi-schema/data-member-array-bad.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.out
diff --git a/tests/qapi-schema/data-member-array.err b/tests/qapi-schema/data-member-array.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.err
diff --git a/tests/qapi-schema/data-member-array.exit b/tests/qapi-schema/data-member-array.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/data-member-array.json b/tests/qapi-schema/data-member-array.json
new file mode 100644
index 0000000000..e6f7f5da13
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.json
@@ -0,0 +1,4 @@
+# valid array members
+{ 'enum': 'abc', 'data': [ 'a', 'b', 'c' ] }
+{ 'struct': 'def', 'data': { 'array': [ 'abc' ] } }
+{ 'command': 'okay', 'data': { 'member1': [ 'int' ], 'member2': [ 'def' ] } }
diff --git a/tests/qapi-schema/data-member-array.out b/tests/qapi-schema/data-member-array.out
new file mode 100644
index 0000000000..c39fa25484
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.out
@@ -0,0 +1,5 @@
+[OrderedDict([('enum', 'abc'), ('data', ['a', 'b', 'c'])]),
+ OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))]),
+ OrderedDict([('command', 'okay'), ('data', OrderedDict([('member1', ['int']), ('member2', ['def'])]))])]
+[{'enum_name': 'abc', 'enum_values': ['a', 'b', 'c']}]
+[OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))])]
diff --git a/tests/qapi-schema/data-member-unknown.err b/tests/qapi-schema/data-member-unknown.err
new file mode 100644
index 0000000000..ab905db802
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/data-member-unknown.exit b/tests/qapi-schema/data-member-unknown.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-member-unknown.json b/tests/qapi-schema/data-member-unknown.json
new file mode 100644
index 0000000000..342a41ec90
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.json
@@ -0,0 +1,2 @@
+# we reject data if it does not contain a known type
+{ 'command': 'oops', 'data': { 'member': 'NoSuchType' } }
diff --git a/tests/qapi-schema/data-member-unknown.out b/tests/qapi-schema/data-member-unknown.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.out
diff --git a/tests/qapi-schema/data-unknown.err b/tests/qapi-schema/data-unknown.err
new file mode 100644
index 0000000000..5b07277a95
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/data-unknown.exit b/tests/qapi-schema/data-unknown.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-unknown.json b/tests/qapi-schema/data-unknown.json
new file mode 100644
index 0000000000..32aba43b3f
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.json
@@ -0,0 +1,2 @@
+# we reject data if it does not contain a known type
+{ 'command': 'oops', 'data': 'NoSuchType' }
diff --git a/tests/qapi-schema/data-unknown.out b/tests/qapi-schema/data-unknown.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.out
diff --git a/tests/qapi-schema/double-data.err b/tests/qapi-schema/double-data.err
new file mode 100644
index 0000000000..cc765c4ff2
--- /dev/null
+++ b/tests/qapi-schema/double-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/double-data.json:2:41: Duplicate key "data"
diff --git a/tests/qapi-schema/double-data.exit b/tests/qapi-schema/double-data.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/double-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/double-data.json b/tests/qapi-schema/double-data.json
new file mode 100644
index 0000000000..e76b519538
--- /dev/null
+++ b/tests/qapi-schema/double-data.json
@@ -0,0 +1,2 @@
+# we reject an expression with duplicate top-level keys
+{ 'struct': 'bar', 'data': { }, 'data': { 'string': 'str'} }
diff --git a/tests/qapi-schema/double-data.out b/tests/qapi-schema/double-data.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/double-data.out
diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err
new file mode 100644
index 0000000000..f9613c6d6b
--- /dev/null
+++ b/tests/qapi-schema/double-type.err
@@ -0,0 +1 @@
+tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar'
diff --git a/tests/qapi-schema/double-type.exit b/tests/qapi-schema/double-type.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/double-type.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/double-type.json b/tests/qapi-schema/double-type.json
new file mode 100644
index 0000000000..911fa7af50
--- /dev/null
+++ b/tests/qapi-schema/double-type.json
@@ -0,0 +1,2 @@
+# we reject an expression with ambiguous metatype
+{ 'command': 'foo', 'struct': 'bar', 'data': { } }
diff --git a/tests/qapi-schema/double-type.out b/tests/qapi-schema/double-type.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/double-type.out
diff --git a/tests/qapi-schema/enum-bad-name.err b/tests/qapi-schema/enum-bad-name.err
new file mode 100644
index 0000000000..9c3c1002b7
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-bad-name.json:2: Member of enum 'MyEnum' uses invalid name 'not^possible'
diff --git a/tests/qapi-schema/enum-bad-name.exit b/tests/qapi-schema/enum-bad-name.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-bad-name.json b/tests/qapi-schema/enum-bad-name.json
new file mode 100644
index 0000000000..8506562b31
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.json
@@ -0,0 +1,2 @@
+# we ensure all enum names can map to C
+{ 'enum': 'MyEnum', 'data': [ 'not^possible' ] }
diff --git a/tests/qapi-schema/enum-bad-name.out b/tests/qapi-schema/enum-bad-name.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.out
diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
new file mode 100644
index 0000000000..48bd1360e7
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-clash-member.json:2: Enum 'MyEnum' member 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/enum-clash-member.exit b/tests/qapi-schema/enum-clash-member.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-clash-member.json b/tests/qapi-schema/enum-clash-member.json
new file mode 100644
index 0000000000..b7dc02a28d
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.json
@@ -0,0 +1,2 @@
+# we reject enums where members will clash when mapped to C enum
+{ 'enum': 'MyEnum', 'data': [ 'one', 'ONE' ] }
diff --git a/tests/qapi-schema/enum-clash-member.out b/tests/qapi-schema/enum-clash-member.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.out
diff --git a/tests/qapi-schema/enum-dict-member.err b/tests/qapi-schema/enum-dict-member.err
new file mode 100644
index 0000000000..8ca146ea59
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/enum-dict-member.exit b/tests/qapi-schema/enum-dict-member.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-dict-member.json b/tests/qapi-schema/enum-dict-member.json
new file mode 100644
index 0000000000..79672e0f09
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.json
@@ -0,0 +1,2 @@
+# we reject any enum member that is not a string
+{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }
diff --git a/tests/qapi-schema/enum-dict-member.out b/tests/qapi-schema/enum-dict-member.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.out
diff --git a/tests/qapi-schema/enum-empty.err b/tests/qapi-schema/enum-empty.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.err
diff --git a/tests/qapi-schema/enum-empty.exit b/tests/qapi-schema/enum-empty.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/enum-empty.json b/tests/qapi-schema/enum-empty.json
new file mode 100644
index 0000000000..40d4e85a2f
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.json
@@ -0,0 +1,2 @@
+# An empty enum, although unusual, is currently acceptable
+{ 'enum': 'MyEnum', 'data': [ ] }
diff --git a/tests/qapi-schema/enum-empty.out b/tests/qapi-schema/enum-empty.out
new file mode 100644
index 0000000000..3b75c1613c
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.out
@@ -0,0 +1,3 @@
+[OrderedDict([('enum', 'MyEnum'), ('data', [])])]
+[{'enum_name': 'MyEnum', 'enum_values': []}]
+[]
diff --git a/tests/qapi-schema/enum-int-member.err b/tests/qapi-schema/enum-int-member.err
new file mode 100644
index 0000000000..071c5213d8
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-int-member.json:3:31: Stray "1"
diff --git a/tests/qapi-schema/enum-int-member.exit b/tests/qapi-schema/enum-int-member.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-int-member.json b/tests/qapi-schema/enum-int-member.json
new file mode 100644
index 0000000000..6c9c32e149
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.json
@@ -0,0 +1,3 @@
+# we reject any enum member that is not a string
+# FIXME: once the parser understands integer inputs, improve the error message
+{ 'enum': 'MyEnum', 'data': [ 1 ] }
diff --git a/tests/qapi-schema/enum-int-member.out b/tests/qapi-schema/enum-int-member.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.out
diff --git a/tests/qapi-schema/enum-max-member.err b/tests/qapi-schema/enum-max-member.err
new file mode 100644
index 0000000000..f77837fb45
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-max-member.json:3: Enum 'MyEnum' member 'max' clashes with '(automatic)'
diff --git a/tests/qapi-schema/enum-max-member.exit b/tests/qapi-schema/enum-max-member.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-max-member.json b/tests/qapi-schema/enum-max-member.json
new file mode 100644
index 0000000000..4bcda0bf07
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.json
@@ -0,0 +1,3 @@
+# we reject user-supplied 'max' for clashing with implicit enum end
+# TODO: should we instead munge the implicit value to avoid the clash?
+{ 'enum': 'MyEnum', 'data': [ 'max' ] }
diff --git a/tests/qapi-schema/enum-max-member.out b/tests/qapi-schema/enum-max-member.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.out
diff --git a/tests/qapi-schema/enum-missing-data.err b/tests/qapi-schema/enum-missing-data.err
new file mode 100644
index 0000000000..ba4873ae69
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-missing-data.json:2: Key 'data' is missing from enum 'MyEnum'
diff --git a/tests/qapi-schema/enum-missing-data.exit b/tests/qapi-schema/enum-missing-data.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-missing-data.json b/tests/qapi-schema/enum-missing-data.json
new file mode 100644
index 0000000000..558fd35e93
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.json
@@ -0,0 +1,2 @@
+# we require that all QAPI enums have a data array
+{ 'enum': 'MyEnum' }
diff --git a/tests/qapi-schema/enum-missing-data.out b/tests/qapi-schema/enum-missing-data.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.out
diff --git a/tests/qapi-schema/enum-union-clash.err b/tests/qapi-schema/enum-union-clash.err
new file mode 100644
index 0000000000..c04e1a8064
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-union-clash.json:2: enum 'UnionKind' should not end in 'Kind'
diff --git a/tests/qapi-schema/enum-union-clash.exit b/tests/qapi-schema/enum-union-clash.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-union-clash.json b/tests/qapi-schema/enum-union-clash.json
new file mode 100644
index 0000000000..593282b6cf
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.json
@@ -0,0 +1,4 @@
+# we reject types that would conflict with implicit union enum
+{ 'enum': 'UnionKind', 'data': [ 'oops' ] }
+{ 'union': 'Union',
+ 'data': { 'a': 'int' } }
diff --git a/tests/qapi-schema/enum-union-clash.out b/tests/qapi-schema/enum-union-clash.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.out
diff --git a/tests/qapi-schema/enum-wrong-data.err b/tests/qapi-schema/enum-wrong-data.err
new file mode 100644
index 0000000000..11b43471cf
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-wrong-data.json:2: Enum 'MyEnum' requires an array for 'data'
diff --git a/tests/qapi-schema/enum-wrong-data.exit b/tests/qapi-schema/enum-wrong-data.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-wrong-data.json b/tests/qapi-schema/enum-wrong-data.json
new file mode 100644
index 0000000000..7b3e255c14
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.json
@@ -0,0 +1,2 @@
+# we require that all qapi enums have an array for data
+{ 'enum': 'MyEnum', 'data': { 'value': 'str' } }
diff --git a/tests/qapi-schema/enum-wrong-data.out b/tests/qapi-schema/enum-wrong-data.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.out
diff --git a/tests/qapi-schema/escape-outside-string.err b/tests/qapi-schema/escape-outside-string.err
new file mode 100644
index 0000000000..b9b8837fd2
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.err
@@ -0,0 +1 @@
+tests/qapi-schema/escape-outside-string.json:3:27: Stray "\"
diff --git a/tests/qapi-schema/escape-outside-string.exit b/tests/qapi-schema/escape-outside-string.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/escape-outside-string.json b/tests/qapi-schema/escape-outside-string.json
new file mode 100644
index 0000000000..482f79554b
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.json
@@ -0,0 +1,3 @@
+# escape sequences are permitted only inside strings
+# { 'command': 'foo', 'data': {} }
+{ 'command': 'foo', 'data'\u003a{} }
diff --git a/tests/qapi-schema/escape-outside-string.out b/tests/qapi-schema/escape-outside-string.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.out
diff --git a/tests/qapi-schema/escape-too-big.err b/tests/qapi-schema/escape-too-big.err
new file mode 100644
index 0000000000..d9aeb5dc38
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.err
@@ -0,0 +1 @@
+tests/qapi-schema/escape-too-big.json:3:14: For now, \u escape only supports non-zero values up to \u007f
diff --git a/tests/qapi-schema/escape-too-big.exit b/tests/qapi-schema/escape-too-big.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/escape-too-big.json b/tests/qapi-schema/escape-too-big.json
new file mode 100644
index 0000000000..62bcecd557
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.json
@@ -0,0 +1,3 @@
+# we don't support full Unicode strings, yet
+# { 'command': 'é' }
+{ 'command': '\u00e9' }
diff --git a/tests/qapi-schema/escape-too-big.out b/tests/qapi-schema/escape-too-big.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.out
diff --git a/tests/qapi-schema/escape-too-short.err b/tests/qapi-schema/escape-too-short.err
new file mode 100644
index 0000000000..934de598ee
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.err
@@ -0,0 +1 @@
+tests/qapi-schema/escape-too-short.json:3:14: \u escape needs 4 hex digits
diff --git a/tests/qapi-schema/escape-too-short.exit b/tests/qapi-schema/escape-too-short.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/escape-too-short.json b/tests/qapi-schema/escape-too-short.json
new file mode 100644
index 0000000000..6cb1dec8f7
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.json
@@ -0,0 +1,3 @@
+# the \u escape requires 4 hex digits
+# { 'command': 'a' }
+{ 'command': '\u61' }
diff --git a/tests/qapi-schema/escape-too-short.out b/tests/qapi-schema/escape-too-short.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.out
diff --git a/tests/qapi-schema/event-case.err b/tests/qapi-schema/event-case.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/event-case.err
diff --git a/tests/qapi-schema/event-case.exit b/tests/qapi-schema/event-case.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/event-case.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/event-case.json b/tests/qapi-schema/event-case.json
new file mode 100644
index 0000000000..3a92d8b610
--- /dev/null
+++ b/tests/qapi-schema/event-case.json
@@ -0,0 +1,3 @@
+# TODO: might be nice to enforce naming conventions; but until then this works
+# even though events should usually be ALL_CAPS
+{ 'event': 'oops' }
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
new file mode 100644
index 0000000000..3764bc781d
--- /dev/null
+++ b/tests/qapi-schema/event-case.out
@@ -0,0 +1,3 @@
+[OrderedDict([('event', 'oops')])]
+[]
+[]
diff --git a/tests/qapi-schema/event-max.err b/tests/qapi-schema/event-max.err
new file mode 100644
index 0000000000..c856534379
--- /dev/null
+++ b/tests/qapi-schema/event-max.err
@@ -0,0 +1 @@
+tests/qapi-schema/event-max.json:2: Event name 'MAX' cannot be created
diff --git a/tests/qapi-schema/event-max.exit b/tests/qapi-schema/event-max.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/event-max.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/event-max.json b/tests/qapi-schema/event-max.json
new file mode 100644
index 0000000000..f3d7de2a30
--- /dev/null
+++ b/tests/qapi-schema/event-max.json
@@ -0,0 +1,2 @@
+# an event named 'MAX' would conflict with implicit C enum
+{ 'event': 'MAX' }
diff --git a/tests/qapi-schema/event-max.out b/tests/qapi-schema/event-max.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/event-max.out
diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err
index 91bde1c967..5a42701b8f 100644
--- a/tests/qapi-schema/event-nest-struct.err
+++ b/tests/qapi-schema/event-nest-struct.err
@@ -1 +1 @@
-tests/qapi-schema/event-nest-struct.json:1: Nested structure define in event is not supported, event 'EVENT_A', argname 'a'
+tests/qapi-schema/event-nest-struct.json:1: Member 'a' of 'data' for event 'EVENT_A' should be a type name
diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err
new file mode 100644
index 0000000000..f9c31b2bf5
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-bad-base.json:9: Flat union 'TestUnion' must have a string base field
diff --git a/tests/qapi-schema/flat-union-bad-base.exit b/tests/qapi-schema/flat-union-bad-base.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-bad-base.json b/tests/qapi-schema/flat-union-bad-base.json
new file mode 100644
index 0000000000..e2e622bb6e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.json
@@ -0,0 +1,13 @@
+# we require the base to be an existing struct
+# TODO: should we allow an anonymous inline base type?
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': { 'enum1': 'TestEnum', 'kind': 'str' },
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'TestTypeA',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-bad-base.out b/tests/qapi-schema/flat-union-bad-base.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.out
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err
new file mode 100644
index 0000000000..c38cc8e4df
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-bad-discriminator.json:11: Discriminator of flat union 'TestUnion' requires a string name
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.exit b/tests/qapi-schema/flat-union-bad-discriminator.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.json b/tests/qapi-schema/flat-union-bad-discriminator.json
new file mode 100644
index 0000000000..cd10b9d901
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.json
@@ -0,0 +1,15 @@
+# we require the discriminator to be a string naming a base-type member
+# this tests the old syntax for anonymous unions before we added alternates
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestBase',
+ 'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'TestBase',
+ 'discriminator': {},
+ 'data': { 'kind1': 'TestTypeA',
+ 'kind2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.out b/tests/qapi-schema/flat-union-bad-discriminator.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.out
diff --git a/tests/qapi-schema/flat-union-base-star.err b/tests/qapi-schema/flat-union-base-star.err
new file mode 100644
index 0000000000..b7748f08bf
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid struct
diff --git a/tests/qapi-schema/flat-union-base-star.exit b/tests/qapi-schema/flat-union-base-star.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-base-star.json b/tests/qapi-schema/flat-union-base-star.json
new file mode 100644
index 0000000000..5099439a9d
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.json
@@ -0,0 +1,12 @@
+# we require the base to be an existing struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': '**',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'TestTypeA',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-base-star.out b/tests/qapi-schema/flat-union-base-star.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.out
diff --git a/tests/qapi-schema/flat-union-base-union.err b/tests/qapi-schema/flat-union-base-union.err
new file mode 100644
index 0000000000..ede9859a39
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-base-union.json:11: Base 'UnionBase' is not a valid struct
diff --git a/tests/qapi-schema/flat-union-base-union.exit b/tests/qapi-schema/flat-union-base-union.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-base-union.json b/tests/qapi-schema/flat-union-base-union.json
new file mode 100644
index 0000000000..6a8ea687a9
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.json
@@ -0,0 +1,15 @@
+# we require the base to be a struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'UnionBase',
+ 'data': { 'kind1': 'TestTypeA',
+ 'kind2': 'TestTypeB' } }
+{ 'union': 'TestUnion',
+ 'base': 'UnionBase',
+ 'discriminator': 'type',
+ 'data': { 'kind1': 'TestTypeA',
+ 'kind2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-base-union.out b/tests/qapi-schema/flat-union-base-union.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.out
diff --git a/tests/qapi-schema/flat-union-branch-clash.err b/tests/qapi-schema/flat-union-branch-clash.err
new file mode 100644
index 0000000000..f11276688c
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-branch-clash.json:10: Member name 'name' of branch 'value1' clashes with base 'Base'
diff --git a/tests/qapi-schema/flat-union-branch-clash.exit b/tests/qapi-schema/flat-union-branch-clash.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-branch-clash.json b/tests/qapi-schema/flat-union-branch-clash.json
new file mode 100644
index 0000000000..8fb054f004
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.json
@@ -0,0 +1,14 @@
+# we check for no duplicate keys between branches and base
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum', '*name': 'str' } }
+{ 'struct': 'Branch1',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Branch2',
+ 'data': { 'value': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'Branch1',
+ 'value2': 'Branch2' } }
diff --git a/tests/qapi-schema/flat-union-branch-clash.out b/tests/qapi-schema/flat-union-branch-clash.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.out
diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err
new file mode 100644
index 0000000000..ec586277b7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-inline.json:7: Flat union 'TestUnion' must have a string base field
diff --git a/tests/qapi-schema/flat-union-inline.exit b/tests/qapi-schema/flat-union-inline.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json
new file mode 100644
index 0000000000..6bfdd65811
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.json
@@ -0,0 +1,11 @@
+# we require branches to be a struct name
+# TODO: should we allow anonymous inline types?
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+{ 'union': 'TestUnion',
+ 'base': { 'enum1': 'TestEnum', 'kind': 'str' },
+ 'discriminator': 'enum1',
+ 'data': { 'value1': { 'string': 'str' },
+ 'value2': { 'integer': 'int' } } }
diff --git a/tests/qapi-schema/flat-union-inline.out b/tests/qapi-schema/flat-union-inline.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.out
diff --git a/tests/qapi-schema/flat-union-int-branch.err b/tests/qapi-schema/flat-union-int-branch.err
new file mode 100644
index 0000000000..faf01573b7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-int-branch.json:8: Member 'value1' of union 'TestUnion' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/flat-union-int-branch.exit b/tests/qapi-schema/flat-union-int-branch.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-int-branch.json b/tests/qapi-schema/flat-union-int-branch.json
new file mode 100644
index 0000000000..9370c349e8
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.json
@@ -0,0 +1,12 @@
+# we require flat union branches to be a struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'int',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-int-branch.out b/tests/qapi-schema/flat-union-int-branch.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.out
diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.json b/tests/qapi-schema/flat-union-invalid-branch-key.json
index a6242823ed..95ff7746bf 100644
--- a/tests/qapi-schema/flat-union-invalid-branch-key.json
+++ b/tests/qapi-schema/flat-union-invalid-branch-key.json
@@ -1,13 +1,13 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum' } }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.err b/tests/qapi-schema/flat-union-invalid-discriminator.err
index 790b6759b8..5f4055614e 100644
--- a/tests/qapi-schema/flat-union-invalid-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-discriminator.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-invalid-discriminator.json:13: Discriminator 'enum_wrong' is not a member of base type 'TestBase'
+tests/qapi-schema/flat-union-invalid-discriminator.json:13: Discriminator 'enum_wrong' is not a member of base struct 'TestBase'
diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.json b/tests/qapi-schema/flat-union-invalid-discriminator.json
index 887157e173..48b94c3a4d 100644
--- a/tests/qapi-schema/flat-union-invalid-discriminator.json
+++ b/tests/qapi-schema/flat-union-invalid-discriminator.json
@@ -1,13 +1,13 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum' } }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err
index a59749eb84..bb3f708747 100644
--- a/tests/qapi-schema/flat-union-no-base.err
+++ b/tests/qapi-schema/flat-union-no-base.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-no-base.json:7: Flat union 'TestUnion' must have a base field
+tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a string base field
diff --git a/tests/qapi-schema/flat-union-no-base.json b/tests/qapi-schema/flat-union-no-base.json
index 50f267323b..ffc4c6f0e6 100644
--- a/tests/qapi-schema/flat-union-no-base.json
+++ b/tests/qapi-schema/flat-union-no-base.json
@@ -1,10 +1,12 @@
-{ 'type': 'TestTypeA',
+# flat unions require a base
+# TODO: simple unions should be able to use an enum discriminator
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
-
+{ 'enum': 'Enum',
+ 'data': [ 'value1', 'value2' ] }
{ 'union': 'TestUnion',
- 'discriminator': 'enum1',
+ 'discriminator': 'Enum',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err
new file mode 100644
index 0000000000..aaabedb3bd
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-optional-discriminator.json:6: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.exit b/tests/qapi-schema/flat-union-optional-discriminator.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.json b/tests/qapi-schema/flat-union-optional-discriminator.json
new file mode 100644
index 0000000000..08a8f7ef8b
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.json
@@ -0,0 +1,10 @@
+# we require the discriminator to be non-optional
+{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
+{ 'struct': 'Base',
+ 'data': { '*switch': 'Enum' } }
+{ 'struct': 'Branch', 'data': { 'name': 'str' } }
+{ 'union': 'MyUnion',
+ 'base': 'Base',
+ 'discriminator': '*switch',
+ 'data': { 'one': 'Branch',
+ 'two': 'Branch' } }
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.out b/tests/qapi-schema/flat-union-optional-discriminator.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.out
diff --git a/tests/qapi-schema/flat-union-reverse-define.json b/tests/qapi-schema/flat-union-reverse-define.json
index 9ea7e72201..648bbfe2b7 100644
--- a/tests/qapi-schema/flat-union-reverse-define.json
+++ b/tests/qapi-schema/flat-union-reverse-define.json
@@ -4,14 +4,14 @@
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum' } }
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
diff --git a/tests/qapi-schema/flat-union-reverse-define.out b/tests/qapi-schema/flat-union-reverse-define.out
index 03c952e28a..1ed7b8a519 100644
--- a/tests/qapi-schema/flat-union-reverse-define.out
+++ b/tests/qapi-schema/flat-union-reverse-define.out
@@ -1,9 +1,9 @@
[OrderedDict([('union', 'TestUnion'), ('base', 'TestBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'TestTypeA'), ('value2', 'TestTypeB')]))]),
- OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
+ OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
- OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
- OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
+ OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
-[OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
- OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
- OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
+[OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
+ OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
diff --git a/tests/qapi-schema/flat-union-string-discriminator.json b/tests/qapi-schema/flat-union-string-discriminator.json
index e966aeb395..8af60333b6 100644
--- a/tests/qapi-schema/flat-union-string-discriminator.json
+++ b/tests/qapi-schema/flat-union-string-discriminator.json
@@ -1,13 +1,13 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
diff --git a/tests/qapi-schema/ident-with-escape.err b/tests/qapi-schema/ident-with-escape.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.err
diff --git a/tests/qapi-schema/ident-with-escape.exit b/tests/qapi-schema/ident-with-escape.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/ident-with-escape.json b/tests/qapi-schema/ident-with-escape.json
new file mode 100644
index 0000000000..56617501e7
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.json
@@ -0,0 +1,4 @@
+# we allow escape sequences in strings, if they map back to ASCII
+# { 'command': 'fooA', 'data': { 'bar1': 'str' } }
+{ 'c\u006fmmand': '\u0066\u006f\u006FA',
+ 'd\u0061ta': { '\u0062\u0061\u00721': '\u0073\u0074\u0072' } }
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
new file mode 100644
index 0000000000..402843081b
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -0,0 +1,3 @@
+[OrderedDict([('command', 'fooA'), ('data', OrderedDict([('bar1', 'str')]))])]
+[]
+[]
diff --git a/tests/qapi-schema/indented-expr.json b/tests/qapi-schema/indented-expr.json
index d80af60564..7115d3131e 100644
--- a/tests/qapi-schema/indented-expr.json
+++ b/tests/qapi-schema/indented-expr.json
@@ -1,2 +1,2 @@
-{ 'id' : 'eins' }
- { 'id' : 'zwei' }
+{ 'command' : 'eins' }
+ { 'command' : 'zwei' }
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index 98af89aa1d..b5ce9151bc 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,3 +1,3 @@
-[OrderedDict([('id', 'eins')]), OrderedDict([('id', 'zwei')])]
+[OrderedDict([('command', 'eins')]), OrderedDict([('command', 'zwei')])]
[]
[]
diff --git a/tests/qapi-schema/missing-type.err b/tests/qapi-schema/missing-type.err
new file mode 100644
index 0000000000..b3e7b14e42
--- /dev/null
+++ b/tests/qapi-schema/missing-type.err
@@ -0,0 +1 @@
+tests/qapi-schema/missing-type.json:2: Expression is missing metatype
diff --git a/tests/qapi-schema/missing-type.exit b/tests/qapi-schema/missing-type.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/missing-type.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/missing-type.json b/tests/qapi-schema/missing-type.json
new file mode 100644
index 0000000000..ff5349d3fe
--- /dev/null
+++ b/tests/qapi-schema/missing-type.json
@@ -0,0 +1,2 @@
+# we reject an expression with missing metatype
+{ 'data': { } }
diff --git a/tests/qapi-schema/missing-type.out b/tests/qapi-schema/missing-type.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/missing-type.out
diff --git a/tests/qapi-schema/nested-struct-data.err b/tests/qapi-schema/nested-struct-data.err
new file mode 100644
index 0000000000..da767bade2
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/nested-struct-data.json:2: Member 'a' of 'data' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/nested-struct-data.exit b/tests/qapi-schema/nested-struct-data.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/nested-struct-data.json b/tests/qapi-schema/nested-struct-data.json
new file mode 100644
index 0000000000..3d52d2b398
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.json
@@ -0,0 +1,4 @@
+# inline subtypes collide with our desired future use of defaults
+{ 'command': 'foo',
+ 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' },
+ 'returns': {} }
diff --git a/tests/qapi-schema/nested-struct-data.out b/tests/qapi-schema/nested-struct-data.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.out
diff --git a/tests/qapi-schema/nested-struct-returns.err b/tests/qapi-schema/nested-struct-returns.err
new file mode 100644
index 0000000000..5238d075b7
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.err
@@ -0,0 +1 @@
+tests/qapi-schema/nested-struct-returns.json:2: Member 'a' of 'returns' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/nested-struct-returns.exit b/tests/qapi-schema/nested-struct-returns.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/nested-struct-returns.json b/tests/qapi-schema/nested-struct-returns.json
new file mode 100644
index 0000000000..d2cd047f0d
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.json
@@ -0,0 +1,3 @@
+# inline subtypes collide with our desired future use of defaults
+{ 'command': 'foo',
+ 'returns': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
diff --git a/tests/qapi-schema/nested-struct-returns.out b/tests/qapi-schema/nested-struct-returns.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.out
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index d43b5fd2e9..8193dc13a9 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -3,44 +3,40 @@
# for testing enums
{ 'enum': 'EnumOne',
'data': [ 'value1', 'value2', 'value3' ] }
-{ 'type': 'NestedEnumsOne',
+{ 'struct': 'NestedEnumsOne',
'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
# for testing nested structs
-{ 'type': 'UserDefZero',
+{ 'struct': 'UserDefZero',
'data': { 'integer': 'int' } }
-{ 'type': 'UserDefOne',
+{ 'struct': 'UserDefOne',
'base': 'UserDefZero',
'data': { 'string': 'str', '*enum1': 'EnumOne' } }
-{ 'type': 'UserDefTwo',
- 'data': { 'string': 'str',
- 'dict': { 'string': 'str',
- 'dict': { 'userdef': 'UserDefOne', 'string': 'str' },
- '*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } }
+{ 'struct': 'UserDefTwoDictDict',
+ 'data': { 'userdef': 'UserDefOne', 'string': 'str' } }
-{ 'type': 'UserDefNested',
+{ 'struct': 'UserDefTwoDict',
+ 'data': { 'string1': 'str',
+ 'dict2': 'UserDefTwoDictDict',
+ '*dict3': 'UserDefTwoDictDict' } }
+
+{ 'struct': 'UserDefTwo',
'data': { 'string0': 'str',
- 'dict1': { 'string1': 'str',
- 'dict2': { 'userdef1': 'UserDefOne', 'string2': 'str' },
- '*dict3': { 'userdef2': 'UserDefOne', 'string3': 'str' } } } }
+ 'dict1': 'UserDefTwoDict' } }
# for testing unions
-{ 'type': 'UserDefA',
+{ 'struct': 'UserDefA',
'data': { 'boolean': 'bool' } }
-{ 'type': 'UserDefB',
+{ 'struct': 'UserDefB',
'data': { 'integer': 'int' } }
-{ 'type': 'UserDefC',
+{ 'struct': 'UserDefC',
'data': { 'string1': 'str', 'string2': 'str' } }
-{ 'union': 'UserDefUnion',
- 'base': 'UserDefZero',
- 'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } }
-
-{ 'type': 'UserDefUnionBase',
+{ 'struct': 'UserDefUnionBase',
'data': { 'string': 'str', 'enum1': 'EnumOne' } }
{ 'union': 'UserDefFlatUnion',
@@ -57,8 +53,7 @@
'discriminator': 'enum1',
'data': { 'value1' : 'UserDefC', 'value2' : 'UserDefB', 'value3' : 'UserDefA' } }
-{ 'union': 'UserDefAnonUnion',
- 'discriminator': {},
+{ 'alternate': 'UserDefAlternate',
'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } }
# for testing native lists
@@ -74,7 +69,8 @@
'u64': ['uint64'],
'number': ['number'],
'boolean': ['bool'],
- 'string': ['str'] } }
+ 'string': ['str'],
+ 'sizes': ['size'] } }
# testing commands
{ 'command': 'user_def_cmd', 'data': {} }
@@ -92,7 +88,7 @@
#
# For simplicity, this example doesn't use [type=]discriminator nor optargs
# specific to discriminator values.
-{ 'type': 'UserDefOptions',
+{ 'struct': 'UserDefOptions',
'data': {
'*i64' : [ 'int' ],
'*u64' : [ 'uint64' ],
@@ -101,7 +97,7 @@
'*u64x': 'uint64' } }
# testing event
-{ 'type': 'EventStructOne',
+{ 'struct': 'EventStructOne',
'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
{ 'event': 'EVENT_A' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 08d7304dfa..93c49635eb 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,40 +1,40 @@
[OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]),
- OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
- OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
- OrderedDict([('union', 'UserDefUnion'), ('base', 'UserDefZero'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]),
- OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]),
+ OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]),
+ OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]),
+ OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
+ OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
+ OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefA'), ('value2', 'UserDefB'), ('value3', 'UserDefB')]))]),
OrderedDict([('union', 'UserDefFlatUnion2'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefC'), ('value2', 'UserDefB'), ('value3', 'UserDefA')]))]),
- OrderedDict([('union', 'UserDefAnonUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]),
- OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str'])]))]),
+ OrderedDict([('alternate', 'UserDefAlternate'), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]),
+ OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str']), ('sizes', ['size'])]))]),
OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]),
OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]),
OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]),
OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]),
- OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
- OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
+ OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]),
OrderedDict([('event', 'EVENT_A')]),
OrderedDict([('event', 'EVENT_B'), ('data', OrderedDict())]),
OrderedDict([('event', 'EVENT_C'), ('data', OrderedDict([('*a', 'int'), ('*b', 'UserDefOne'), ('c', 'str')]))]),
OrderedDict([('event', 'EVENT_D'), ('data', OrderedDict([('a', 'EventStructOne'), ('b', 'str'), ('*c', 'str'), ('*enum3', 'EnumOne')]))])]
[{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']},
- {'enum_name': 'UserDefUnionKind', 'enum_values': None},
- {'enum_name': 'UserDefAnonUnionKind', 'enum_values': None},
+ {'enum_name': 'UserDefAlternateKind', 'enum_values': None},
{'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None}]
-[OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
- OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
- OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
- OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))])]
+[OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]),
+ OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]),
+ OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]),
+ OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
+ OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
+ OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
+ OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))])]
diff --git a/tests/qapi-schema/redefined-builtin.err b/tests/qapi-schema/redefined-builtin.err
new file mode 100644
index 0000000000..b2757225c4
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-builtin.json:2: built-in 'size' is already defined
diff --git a/tests/qapi-schema/redefined-builtin.exit b/tests/qapi-schema/redefined-builtin.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-builtin.json b/tests/qapi-schema/redefined-builtin.json
new file mode 100644
index 0000000000..45b8a550ad
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.json
@@ -0,0 +1,2 @@
+# we reject types that duplicate builtin names
+{ 'struct': 'size', 'data': { 'myint': 'size' } }
diff --git a/tests/qapi-schema/redefined-builtin.out b/tests/qapi-schema/redefined-builtin.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.out
diff --git a/tests/qapi-schema/redefined-command.err b/tests/qapi-schema/redefined-command.err
new file mode 100644
index 0000000000..82ae256e63
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-command.json:3: command 'foo' is already defined
diff --git a/tests/qapi-schema/redefined-command.exit b/tests/qapi-schema/redefined-command.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-command.json b/tests/qapi-schema/redefined-command.json
new file mode 100644
index 0000000000..247e401948
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.json
@@ -0,0 +1,3 @@
+# we reject commands defined more than once
+{ 'command': 'foo', 'data': { 'one': 'str' } }
+{ 'command': 'foo', 'data': { '*two': 'str' } }
diff --git a/tests/qapi-schema/redefined-command.out b/tests/qapi-schema/redefined-command.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.out
diff --git a/tests/qapi-schema/redefined-event.err b/tests/qapi-schema/redefined-event.err
new file mode 100644
index 0000000000..35429cb481
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-event.json:3: event 'EVENT_A' is already defined
diff --git a/tests/qapi-schema/redefined-event.exit b/tests/qapi-schema/redefined-event.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-event.json b/tests/qapi-schema/redefined-event.json
new file mode 100644
index 0000000000..7717e91c18
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.json
@@ -0,0 +1,3 @@
+# we reject duplicate events
+{ 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
+{ 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
diff --git a/tests/qapi-schema/redefined-event.out b/tests/qapi-schema/redefined-event.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.out
diff --git a/tests/qapi-schema/redefined-type.err b/tests/qapi-schema/redefined-type.err
new file mode 100644
index 0000000000..06ea78c478
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-type.json:3: struct 'foo' is already defined
diff --git a/tests/qapi-schema/redefined-type.exit b/tests/qapi-schema/redefined-type.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-type.json b/tests/qapi-schema/redefined-type.json
new file mode 100644
index 0000000000..a09e768bae
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.json
@@ -0,0 +1,3 @@
+# we reject types defined more than once
+{ 'struct': 'foo', 'data': { 'one': 'str' } }
+{ 'enum': 'foo', 'data': [ 'two' ] }
diff --git a/tests/qapi-schema/redefined-type.out b/tests/qapi-schema/redefined-type.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.out
diff --git a/tests/qapi-schema/returns-alternate.err b/tests/qapi-schema/returns-alternate.err
new file mode 100644
index 0000000000..dfbb419cac
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-alternate.json:3: 'returns' for command 'oops' cannot use alternate type 'Alt'
diff --git a/tests/qapi-schema/returns-alternate.exit b/tests/qapi-schema/returns-alternate.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-alternate.json b/tests/qapi-schema/returns-alternate.json
new file mode 100644
index 0000000000..972390c06b
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.json
@@ -0,0 +1,3 @@
+# we reject returns if it is an alternate type
+{ 'alternate': 'Alt', 'data': { 'a': 'int', 'b': 'str' } }
+{ 'command': 'oops', 'returns': 'Alt' }
diff --git a/tests/qapi-schema/returns-alternate.out b/tests/qapi-schema/returns-alternate.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.out
diff --git a/tests/qapi-schema/returns-array-bad.err b/tests/qapi-schema/returns-array-bad.err
new file mode 100644
index 0000000000..138095ccde
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-array-bad.json:2: 'returns' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/returns-array-bad.exit b/tests/qapi-schema/returns-array-bad.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-array-bad.json b/tests/qapi-schema/returns-array-bad.json
new file mode 100644
index 0000000000..09b0b1f182
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.json
@@ -0,0 +1,2 @@
+# we reject an array return that is not a single type
+{ 'command': 'oops', 'returns': [ 'str', 'str' ] }
diff --git a/tests/qapi-schema/returns-array-bad.out b/tests/qapi-schema/returns-array-bad.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.out
diff --git a/tests/qapi-schema/returns-int.err b/tests/qapi-schema/returns-int.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/returns-int.err
diff --git a/tests/qapi-schema/returns-int.exit b/tests/qapi-schema/returns-int.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/returns-int.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/returns-int.json b/tests/qapi-schema/returns-int.json
new file mode 100644
index 0000000000..870ec6366b
--- /dev/null
+++ b/tests/qapi-schema/returns-int.json
@@ -0,0 +1,3 @@
+# It is okay (although not extensible) to return a non-dictionary
+# But to make it work, the name must be in a whitelist
+{ 'command': 'guest-get-time', 'returns': 'int' }
diff --git a/tests/qapi-schema/returns-int.out b/tests/qapi-schema/returns-int.out
new file mode 100644
index 0000000000..70b3ac5e6f
--- /dev/null
+++ b/tests/qapi-schema/returns-int.out
@@ -0,0 +1,3 @@
+[OrderedDict([('command', 'guest-get-time'), ('returns', 'int')])]
+[]
+[]
diff --git a/tests/qapi-schema/returns-unknown.err b/tests/qapi-schema/returns-unknown.err
new file mode 100644
index 0000000000..1f43e3ac9f
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-unknown.json:2: 'returns' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/returns-unknown.exit b/tests/qapi-schema/returns-unknown.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-unknown.json b/tests/qapi-schema/returns-unknown.json
new file mode 100644
index 0000000000..25bd498bff
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.json
@@ -0,0 +1,2 @@
+# we reject returns if it does not contain a known type
+{ 'command': 'oops', 'returns': 'NoSuchType' }
diff --git a/tests/qapi-schema/returns-unknown.out b/tests/qapi-schema/returns-unknown.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.out
diff --git a/tests/qapi-schema/returns-whitelist.err b/tests/qapi-schema/returns-whitelist.err
new file mode 100644
index 0000000000..a41f019a52
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'array of int'
diff --git a/tests/qapi-schema/returns-whitelist.exit b/tests/qapi-schema/returns-whitelist.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-whitelist.json b/tests/qapi-schema/returns-whitelist.json
new file mode 100644
index 0000000000..e8b3cea396
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.json
@@ -0,0 +1,11 @@
+# we enforce that 'returns' be a dict or array of dict unless whitelisted
+{ 'command': 'human-monitor-command',
+ 'data': {'command-line': 'str', '*cpu-index': 'int'},
+ 'returns': 'str' }
+{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
+{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
+{ 'command': 'guest-get-time',
+ 'returns': 'int' }
+
+{ 'command': 'no-way-this-will-get-whitelisted',
+ 'returns': [ 'int' ] }
diff --git a/tests/qapi-schema/returns-whitelist.out b/tests/qapi-schema/returns-whitelist.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.out
diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err
new file mode 100644
index 0000000000..e3e9f8d289
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-base-clash-deep.json:7: Member name 'name' clashes with base 'Base'
diff --git a/tests/qapi-schema/struct-base-clash-deep.exit b/tests/qapi-schema/struct-base-clash-deep.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/struct-base-clash-deep.json b/tests/qapi-schema/struct-base-clash-deep.json
new file mode 100644
index 0000000000..552fe94317
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.json
@@ -0,0 +1,9 @@
+# we check for no duplicate keys with indirect base
+{ 'struct': 'Base',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Mid',
+ 'base': 'Base',
+ 'data': { 'value': 'int' } }
+{ 'struct': 'Sub',
+ 'base': 'Mid',
+ 'data': { '*name': 'str' } }
diff --git a/tests/qapi-schema/struct-base-clash-deep.out b/tests/qapi-schema/struct-base-clash-deep.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.out
diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err
new file mode 100644
index 0000000000..3ac37fb26a
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-base-clash.json:4: Member name 'name' clashes with base 'Base'
diff --git a/tests/qapi-schema/struct-base-clash.exit b/tests/qapi-schema/struct-base-clash.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/struct-base-clash.json b/tests/qapi-schema/struct-base-clash.json
new file mode 100644
index 0000000000..f2afc9b6f6
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.json
@@ -0,0 +1,6 @@
+# we check for no duplicate keys with base
+{ 'struct': 'Base',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Sub',
+ 'base': 'Base',
+ 'data': { 'name': 'str' } }
diff --git a/tests/qapi-schema/struct-base-clash.out b/tests/qapi-schema/struct-base-clash.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.out
diff --git a/tests/qapi-schema/type-bypass-bad-gen.err b/tests/qapi-schema/type-bypass-bad-gen.err
new file mode 100644
index 0000000000..a83c3c655d
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.err
@@ -0,0 +1 @@
+tests/qapi-schema/type-bypass-bad-gen.json:2: 'gen' of command 'foo' should only use false value
diff --git a/tests/qapi-schema/type-bypass-bad-gen.exit b/tests/qapi-schema/type-bypass-bad-gen.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/type-bypass-bad-gen.json b/tests/qapi-schema/type-bypass-bad-gen.json
new file mode 100644
index 0000000000..e8dec34249
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.json
@@ -0,0 +1,2 @@
+# 'gen' should only appear with value false
+{ 'command': 'foo', 'gen': 'whatever' }
diff --git a/tests/qapi-schema/type-bypass-bad-gen.out b/tests/qapi-schema/type-bypass-bad-gen.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.out
diff --git a/tests/qapi-schema/type-bypass-no-gen.err b/tests/qapi-schema/type-bypass-no-gen.err
new file mode 100644
index 0000000000..20cef0a8a7
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.err
@@ -0,0 +1 @@
+tests/qapi-schema/type-bypass-no-gen.json:2: Member 'arg' of 'data' for command 'unsafe' uses '**' but did not request 'gen':false
diff --git a/tests/qapi-schema/type-bypass-no-gen.exit b/tests/qapi-schema/type-bypass-no-gen.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/type-bypass-no-gen.json b/tests/qapi-schema/type-bypass-no-gen.json
new file mode 100644
index 0000000000..4feae3719c
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.json
@@ -0,0 +1,2 @@
+# type bypass only works with 'gen':false
+{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**' }
diff --git a/tests/qapi-schema/type-bypass-no-gen.out b/tests/qapi-schema/type-bypass-no-gen.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.out
diff --git a/tests/qapi-schema/type-bypass.err b/tests/qapi-schema/type-bypass.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.err
diff --git a/tests/qapi-schema/type-bypass.exit b/tests/qapi-schema/type-bypass.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/type-bypass.json b/tests/qapi-schema/type-bypass.json
new file mode 100644
index 0000000000..48b2137833
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.json
@@ -0,0 +1,2 @@
+# Use of 'gen':false allows bypassing type system
+{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**', 'gen': false }
diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out
new file mode 100644
index 0000000000..eaf20f8344
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.out
@@ -0,0 +1,3 @@
+[OrderedDict([('command', 'unsafe'), ('data', OrderedDict([('arg', '**')])), ('returns', '**'), ('gen', False)])]
+[]
+[]
diff --git a/tests/qapi-schema/unicode-str.err b/tests/qapi-schema/unicode-str.err
new file mode 100644
index 0000000000..f621cd6448
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.err
@@ -0,0 +1 @@
+tests/qapi-schema/unicode-str.json:2: 'command' uses invalid name 'é'
diff --git a/tests/qapi-schema/unicode-str.exit b/tests/qapi-schema/unicode-str.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/unicode-str.json b/tests/qapi-schema/unicode-str.json
new file mode 100644
index 0000000000..5253a1b9f3
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.json
@@ -0,0 +1,2 @@
+# we don't support full Unicode strings, yet
+{ 'command': 'é' }
diff --git a/tests/qapi-schema/unicode-str.out b/tests/qapi-schema/unicode-str.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.out
diff --git a/tests/qapi-schema/union-bad-branch.err b/tests/qapi-schema/union-bad-branch.err
new file mode 100644
index 0000000000..8822735561
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-bad-branch.json:6: Union 'MyUnion' member 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/union-bad-branch.exit b/tests/qapi-schema/union-bad-branch.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-bad-branch.json b/tests/qapi-schema/union-bad-branch.json
new file mode 100644
index 0000000000..913aa38bc8
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.json
@@ -0,0 +1,8 @@
+# we reject normal unions where branches would collide in C
+{ 'struct': 'One',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'Two',
+ 'data': { 'number': 'int' } }
+{ 'union': 'MyUnion',
+ 'data': { 'one': 'One',
+ 'ONE': 'Two' } }
diff --git a/tests/qapi-schema/union-bad-branch.out b/tests/qapi-schema/union-bad-branch.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.out
diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err
new file mode 100644
index 0000000000..fc8b79c459
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-base-no-discriminator.json:11: Union 'TestUnion' requires a discriminator to go along with base
diff --git a/tests/qapi-schema/union-base-no-discriminator.exit b/tests/qapi-schema/union-base-no-discriminator.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-base-no-discriminator.json b/tests/qapi-schema/union-base-no-discriminator.json
new file mode 100644
index 0000000000..1409cf5c9e
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.json
@@ -0,0 +1,14 @@
+# we reject simple unions with a base (or flat unions without discriminator)
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+
+{ 'struct': 'Base',
+ 'data': { 'string': 'str' } }
+
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'data': { 'value1': 'TestTypeA',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-base-no-discriminator.out b/tests/qapi-schema/union-base-no-discriminator.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.out
diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err
index 938f96962b..9f637963e8 100644
--- a/tests/qapi-schema/union-invalid-base.err
+++ b/tests/qapi-schema/union-invalid-base.err
@@ -1 +1 @@
-tests/qapi-schema/union-invalid-base.json:7: Base 'TestBaseWrong' is not a valid type
+tests/qapi-schema/union-invalid-base.json:8: Base 'int' is not a valid struct
diff --git a/tests/qapi-schema/union-invalid-base.json b/tests/qapi-schema/union-invalid-base.json
index 1fa4930010..92be39df69 100644
--- a/tests/qapi-schema/union-invalid-base.json
+++ b/tests/qapi-schema/union-invalid-base.json
@@ -1,10 +1,12 @@
-{ 'type': 'TestTypeA',
+# a union base type must be a struct
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
- 'base': 'TestBaseWrong',
+ 'base': 'int',
+ 'discriminator': 'int',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-max.err b/tests/qapi-schema/union-max.err
new file mode 100644
index 0000000000..55ce4399d6
--- /dev/null
+++ b/tests/qapi-schema/union-max.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-max.json:2: Union 'Union' member 'max' clashes with '(automatic)'
diff --git a/tests/qapi-schema/union-max.exit b/tests/qapi-schema/union-max.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/union-max.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-max.json b/tests/qapi-schema/union-max.json
new file mode 100644
index 0000000000..d6ad986999
--- /dev/null
+++ b/tests/qapi-schema/union-max.json
@@ -0,0 +1,3 @@
+# we reject 'max' branch in a union, for collision with C enum
+{ 'union': 'Union',
+ 'data': { 'max': 'int' } }
diff --git a/tests/qapi-schema/union-max.out b/tests/qapi-schema/union-max.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/union-max.out
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
new file mode 100644
index 0000000000..3ada1334dc
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-optional-branch.json:2: Member of union 'Union' does not allow optional name '*a'
diff --git a/tests/qapi-schema/union-optional-branch.exit b/tests/qapi-schema/union-optional-branch.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-optional-branch.json b/tests/qapi-schema/union-optional-branch.json
new file mode 100644
index 0000000000..591615fc68
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.json
@@ -0,0 +1,2 @@
+# union branches cannot be optional
+{ 'union': 'Union', 'data': { '*a': 'int', 'b': 'str' } }
diff --git a/tests/qapi-schema/union-optional-branch.out b/tests/qapi-schema/union-optional-branch.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.out
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
new file mode 100644
index 0000000000..54fe456f9c
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-unknown.json:2: Member 'unknown' of union 'Union' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/union-unknown.exit b/tests/qapi-schema/union-unknown.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-unknown.json b/tests/qapi-schema/union-unknown.json
new file mode 100644
index 0000000000..aa7e8143d8
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.json
@@ -0,0 +1,3 @@
+# we reject a union with unknown type in branch
+{ 'union': 'Union',
+ 'data': { 'unknown': 'MissingType' } }
diff --git a/tests/qapi-schema/union-unknown.out b/tests/qapi-schema/union-unknown.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.out
diff --git a/tests/qapi-schema/unknown-escape.err b/tests/qapi-schema/unknown-escape.err
new file mode 100644
index 0000000000..000e30ddf3
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.err
@@ -0,0 +1 @@
+tests/qapi-schema/unknown-escape.json:3:21: Unknown escape \x
diff --git a/tests/qapi-schema/unknown-escape.exit b/tests/qapi-schema/unknown-escape.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/unknown-escape.json b/tests/qapi-schema/unknown-escape.json
new file mode 100644
index 0000000000..8e6891e52a
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.json
@@ -0,0 +1,3 @@
+# we only recognize JSON escape sequences, plus our \' extension (no \x)
+# { 'command': 'foo', 'data': {} }
+{ 'command': 'foo', 'dat\x61':{} }
diff --git a/tests/qapi-schema/unknown-escape.out b/tests/qapi-schema/unknown-escape.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.out
diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err
new file mode 100644
index 0000000000..12f5ed5b43
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.err
@@ -0,0 +1 @@
+tests/qapi-schema/unknown-expr-key.json:2: Unknown key 'bogus' in struct 'bar'
diff --git a/tests/qapi-schema/unknown-expr-key.exit b/tests/qapi-schema/unknown-expr-key.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/unknown-expr-key.json b/tests/qapi-schema/unknown-expr-key.json
new file mode 100644
index 0000000000..3b2be00cc4
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.json
@@ -0,0 +1,2 @@
+# we reject an expression with unknown top-level keys
+{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { } }
diff --git a/tests/qapi-schema/unknown-expr-key.out b/tests/qapi-schema/unknown-expr-key.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.out
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 554e222b32..ad2e4030b2 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -31,14 +31,17 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a,
ud1d->base = g_new0(UserDefZero, 1);
ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0;
- ret = g_malloc0(sizeof(UserDefTwo));
- ret->string = strdup("blah1");
- ret->dict.string = strdup("blah2");
- ret->dict.dict.userdef = ud1c;
- ret->dict.dict.string = strdup("blah3");
- ret->dict.has_dict2 = true;
- ret->dict.dict2.userdef = ud1d;
- ret->dict.dict2.string = strdup("blah4");
+ ret = g_new0(UserDefTwo, 1);
+ ret->string0 = strdup("blah1");
+ ret->dict1 = g_new0(UserDefTwoDict, 1);
+ ret->dict1->string1 = strdup("blah2");
+ ret->dict1->dict2 = g_new0(UserDefTwoDictDict, 1);
+ ret->dict1->dict2->userdef = ud1c;
+ ret->dict1->dict2->string = strdup("blah3");
+ ret->dict1->dict3 = g_new0(UserDefTwoDictDict, 1);
+ ret->dict1->has_dict3 = true;
+ ret->dict1->dict3->userdef = ud1d;
+ ret->dict1->dict3->string = strdup("blah4");
return ret;
}
@@ -120,15 +123,15 @@ static void test_dispatch_cmd_io(void)
ret = qobject_to_qdict(test_qmp_dispatch(req));
- assert(!strcmp(qdict_get_str(ret, "string"), "blah1"));
- ret_dict = qdict_get_qdict(ret, "dict");
- assert(!strcmp(qdict_get_str(ret_dict, "string"), "blah2"));
- ret_dict_dict = qdict_get_qdict(ret_dict, "dict");
+ assert(!strcmp(qdict_get_str(ret, "string0"), "blah1"));
+ ret_dict = qdict_get_qdict(ret, "dict1");
+ assert(!strcmp(qdict_get_str(ret_dict, "string1"), "blah2"));
+ ret_dict_dict = qdict_get_qdict(ret_dict, "dict2");
ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef");
assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42);
assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello"));
assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3"));
- ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict2");
+ ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict3");
ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef");
assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422);
assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2"));
@@ -192,7 +195,7 @@ static void test_dealloc_partial(void)
QmpInputVisitor *qiv;
ud2_dict = qdict_new();
- qdict_put_obj(ud2_dict, "string", QOBJECT(qstring_from_str(text)));
+ qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));
qiv = qmp_input_visitor_new(QOBJECT(ud2_dict));
visit_type_UserDefTwo(qmp_input_get_visitor(qiv), &ud2, NULL, &err);
@@ -202,9 +205,9 @@ static void test_dealloc_partial(void)
/* verify partial success */
assert(ud2 != NULL);
- assert(ud2->string != NULL);
- assert(strcmp(ud2->string, text) == 0);
- assert(ud2->dict.dict.userdef == NULL);
+ assert(ud2->string0 != NULL);
+ assert(strcmp(ud2->string0, text) == 0);
+ assert(ud2->dict1 == NULL);
/* confirm & release construction error */
assert(err != NULL);
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index d5360c6a87..68f855bdf3 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -1,7 +1,7 @@
/*
* QMP Input Visitor unit-tests (strict mode).
*
- * Copyright (C) 2011-2012 Red Hat Inc.
+ * Copyright (C) 2011-2012, 2015 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
@@ -116,15 +116,18 @@ static void test_validate_struct(TestInputVisitorData *data,
static void test_validate_struct_nested(TestInputVisitorData *data,
const void *unused)
{
- UserDefNested *udp = NULL;
+ UserDefTwo *udp = NULL;
Error *err = NULL;
Visitor *v;
- v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}");
+ v = validate_test_init(data, "{ 'string0': 'string0', "
+ "'dict1': { 'string1': 'string1', "
+ "'dict2': { 'userdef': { 'integer': 42, "
+ "'string': 'string' }, 'string': 'string2'}}}");
- visit_type_UserDefNested(v, &udp, NULL, &err);
+ visit_type_UserDefTwo(v, &udp, NULL, &err);
g_assert(!err);
- qapi_free_UserDefNested(udp);
+ qapi_free_UserDefTwo(udp);
}
static void test_validate_list(TestInputVisitorData *data,
@@ -141,18 +144,18 @@ static void test_validate_list(TestInputVisitorData *data,
qapi_free_UserDefOneList(head);
}
-static void test_validate_union(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_union_native_list(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefUnion *tmp = NULL;
+ UserDefNativeListUnion *tmp = NULL;
Visitor *v;
Error *err = NULL;
- v = validate_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }");
+ v = validate_test_init(data, "{ 'type': 'integer', 'data' : [ 1, 2 ] }");
- visit_type_UserDefUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err);
g_assert(!err);
- qapi_free_UserDefUnion(tmp);
+ qapi_free_UserDefNativeListUnion(tmp);
}
static void test_validate_union_flat(TestInputVisitorData *data,
@@ -173,18 +176,18 @@ static void test_validate_union_flat(TestInputVisitorData *data,
qapi_free_UserDefFlatUnion(tmp);
}
-static void test_validate_union_anon(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_alternate(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefAnonUnion *tmp = NULL;
+ UserDefAlternate *tmp = NULL;
Visitor *v;
Error *err = NULL;
v = validate_test_init(data, "42");
- visit_type_UserDefAnonUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(v, &tmp, NULL, &err);
g_assert(!err);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void test_validate_fail_struct(TestInputVisitorData *data,
@@ -207,15 +210,15 @@ static void test_validate_fail_struct(TestInputVisitorData *data,
static void test_validate_fail_struct_nested(TestInputVisitorData *data,
const void *unused)
{
- UserDefNested *udp = NULL;
+ UserDefTwo *udp = NULL;
Error *err = NULL;
Visitor *v;
v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}");
- visit_type_UserDefNested(v, &udp, NULL, &err);
+ visit_type_UserDefTwo(v, &udp, NULL, &err);
g_assert(err);
- qapi_free_UserDefNested(udp);
+ qapi_free_UserDefTwo(udp);
}
static void test_validate_fail_list(TestInputVisitorData *data,
@@ -232,18 +235,19 @@ static void test_validate_fail_list(TestInputVisitorData *data,
qapi_free_UserDefOneList(head);
}
-static void test_validate_fail_union(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_fail_union_native_list(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefUnion *tmp = NULL;
+ UserDefNativeListUnion *tmp = NULL;
Error *err = NULL;
Visitor *v;
- v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }");
+ v = validate_test_init(data,
+ "{ 'type': 'integer', 'data' : [ 'string' ] }");
- visit_type_UserDefUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err);
g_assert(err);
- qapi_free_UserDefUnion(tmp);
+ qapi_free_UserDefNativeListUnion(tmp);
}
static void test_validate_fail_union_flat(TestInputVisitorData *data,
@@ -275,18 +279,18 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
qapi_free_UserDefFlatUnion2(tmp);
}
-static void test_validate_fail_union_anon(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_fail_alternate(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefAnonUnion *tmp = NULL;
+ UserDefAlternate *tmp = NULL;
Visitor *v;
Error *err = NULL;
v = validate_test_init(data, "3.14");
- visit_type_UserDefAnonUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(v, &tmp, NULL, &err);
g_assert(err);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void validate_test_add(const char *testpath,
@@ -304,31 +308,31 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
validate_test_add("/visitor/input-strict/pass/struct",
- &testdata, test_validate_struct);
+ &testdata, test_validate_struct);
validate_test_add("/visitor/input-strict/pass/struct-nested",
- &testdata, test_validate_struct_nested);
+ &testdata, test_validate_struct_nested);
validate_test_add("/visitor/input-strict/pass/list",
- &testdata, test_validate_list);
- validate_test_add("/visitor/input-strict/pass/union",
- &testdata, test_validate_union);
+ &testdata, test_validate_list);
validate_test_add("/visitor/input-strict/pass/union-flat",
- &testdata, test_validate_union_flat);
- validate_test_add("/visitor/input-strict/pass/union-anon",
- &testdata, test_validate_union_anon);
+ &testdata, test_validate_union_flat);
+ validate_test_add("/visitor/input-strict/pass/alternate",
+ &testdata, test_validate_alternate);
+ validate_test_add("/visitor/input-strict/pass/union-native-list",
+ &testdata, test_validate_union_native_list);
validate_test_add("/visitor/input-strict/fail/struct",
- &testdata, test_validate_fail_struct);
+ &testdata, test_validate_fail_struct);
validate_test_add("/visitor/input-strict/fail/struct-nested",
- &testdata, test_validate_fail_struct_nested);
+ &testdata, test_validate_fail_struct_nested);
validate_test_add("/visitor/input-strict/fail/list",
- &testdata, test_validate_fail_list);
- validate_test_add("/visitor/input-strict/fail/union",
- &testdata, test_validate_fail_union);
+ &testdata, test_validate_fail_list);
validate_test_add("/visitor/input-strict/fail/union-flat",
- &testdata, test_validate_fail_union_flat);
+ &testdata, test_validate_fail_union_flat);
validate_test_add("/visitor/input-strict/fail/union-flat-no-discriminator",
- &testdata, test_validate_fail_union_flat_no_discrim);
- validate_test_add("/visitor/input-strict/fail/union-anon",
- &testdata, test_validate_fail_union_anon);
+ &testdata, test_validate_fail_union_flat_no_discrim);
+ validate_test_add("/visitor/input-strict/fail/alternate",
+ &testdata, test_validate_fail_alternate);
+ validate_test_add("/visitor/input-strict/fail/union-native-list",
+ &testdata, test_validate_fail_union_native_list);
g_test_run();
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 1c8e87295c..b96195309b 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -1,7 +1,7 @@
/*
* QMP Input Visitor unit-tests.
*
- * Copyright (C) 2011 Red Hat Inc.
+ * Copyright (C) 2011, 2015 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
@@ -248,23 +248,28 @@ static void check_and_free_str(char *str, const char *cmp)
static void test_visitor_in_struct_nested(TestInputVisitorData *data,
const void *unused)
{
- UserDefNested *udp = NULL;
+ UserDefTwo *udp = NULL;
Error *err = NULL;
Visitor *v;
- v = visitor_input_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}");
+ v = visitor_input_test_init(data, "{ 'string0': 'string0', "
+ "'dict1': { 'string1': 'string1', "
+ "'dict2': { 'userdef': { 'integer': 42, "
+ "'string': 'string' }, 'string': 'string2'}}}");
- visit_type_UserDefNested(v, &udp, NULL, &err);
+ visit_type_UserDefTwo(v, &udp, NULL, &err);
g_assert(!err);
check_and_free_str(udp->string0, "string0");
- check_and_free_str(udp->dict1.string1, "string1");
- g_assert_cmpint(udp->dict1.dict2.userdef1->base->integer, ==, 42);
- check_and_free_str(udp->dict1.dict2.userdef1->string, "string");
- check_and_free_str(udp->dict1.dict2.string2, "string2");
- g_assert(udp->dict1.has_dict3 == false);
-
- g_free(udp->dict1.dict2.userdef1);
+ check_and_free_str(udp->dict1->string1, "string1");
+ g_assert_cmpint(udp->dict1->dict2->userdef->base->integer, ==, 42);
+ check_and_free_str(udp->dict1->dict2->userdef->string, "string");
+ check_and_free_str(udp->dict1->dict2->string, "string2");
+ g_assert(udp->dict1->has_dict3 == false);
+
+ g_free(udp->dict1->dict2->userdef);
+ g_free(udp->dict1->dict2);
+ g_free(udp->dict1);
g_free(udp);
}
@@ -293,23 +298,6 @@ static void test_visitor_in_list(TestInputVisitorData *data,
qapi_free_UserDefOneList(head);
}
-static void test_visitor_in_union(TestInputVisitorData *data,
- const void *unused)
-{
- Visitor *v;
- Error *err = NULL;
- UserDefUnion *tmp;
-
- v = visitor_input_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }");
-
- visit_type_UserDefUnion(v, &tmp, NULL, &err);
- g_assert(err == NULL);
- g_assert_cmpint(tmp->kind, ==, USER_DEF_UNION_KIND_B);
- g_assert_cmpint(tmp->integer, ==, 41);
- g_assert_cmpint(tmp->b->integer, ==, 42);
- qapi_free_UserDefUnion(tmp);
-}
-
static void test_visitor_in_union_flat(TestInputVisitorData *data,
const void *unused)
{
@@ -332,20 +320,20 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
qapi_free_UserDefFlatUnion(tmp);
}
-static void test_visitor_in_union_anon(TestInputVisitorData *data,
- const void *unused)
+static void test_visitor_in_alternate(TestInputVisitorData *data,
+ const void *unused)
{
Visitor *v;
Error *err = NULL;
- UserDefAnonUnion *tmp;
+ UserDefAlternate *tmp;
v = visitor_input_test_init(data, "42");
- visit_type_UserDefAnonUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(v, &tmp, NULL, &err);
g_assert(err == NULL);
- g_assert_cmpint(tmp->kind, ==, USER_DEF_ANON_UNION_KIND_I);
+ g_assert_cmpint(tmp->kind, ==, USER_DEF_ALTERNATE_KIND_I);
g_assert_cmpint(tmp->i, ==, 42);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void test_native_list_integer_helper(TestInputVisitorData *data,
@@ -670,55 +658,56 @@ int main(int argc, char **argv)
input_visitor_test_add("/visitor/input/number",
&in_visitor_data, test_visitor_in_number);
input_visitor_test_add("/visitor/input/string",
- &in_visitor_data, test_visitor_in_string);
+ &in_visitor_data, test_visitor_in_string);
input_visitor_test_add("/visitor/input/enum",
- &in_visitor_data, test_visitor_in_enum);
+ &in_visitor_data, test_visitor_in_enum);
input_visitor_test_add("/visitor/input/struct",
- &in_visitor_data, test_visitor_in_struct);
+ &in_visitor_data, test_visitor_in_struct);
input_visitor_test_add("/visitor/input/struct-nested",
- &in_visitor_data, test_visitor_in_struct_nested);
+ &in_visitor_data, test_visitor_in_struct_nested);
input_visitor_test_add("/visitor/input/list",
- &in_visitor_data, test_visitor_in_list);
- input_visitor_test_add("/visitor/input/union",
- &in_visitor_data, test_visitor_in_union);
+ &in_visitor_data, test_visitor_in_list);
input_visitor_test_add("/visitor/input/union-flat",
- &in_visitor_data, test_visitor_in_union_flat);
- input_visitor_test_add("/visitor/input/union-anon",
- &in_visitor_data, test_visitor_in_union_anon);
+ &in_visitor_data, test_visitor_in_union_flat);
+ input_visitor_test_add("/visitor/input/alternate",
+ &in_visitor_data, test_visitor_in_alternate);
input_visitor_test_add("/visitor/input/errors",
- &in_visitor_data, test_visitor_in_errors);
+ &in_visitor_data, test_visitor_in_errors);
input_visitor_test_add("/visitor/input/native_list/int",
- &in_visitor_data,
- test_visitor_in_native_list_int);
+ &in_visitor_data,
+ test_visitor_in_native_list_int);
input_visitor_test_add("/visitor/input/native_list/int8",
- &in_visitor_data,
- test_visitor_in_native_list_int8);
+ &in_visitor_data,
+ test_visitor_in_native_list_int8);
input_visitor_test_add("/visitor/input/native_list/int16",
- &in_visitor_data,
- test_visitor_in_native_list_int16);
+ &in_visitor_data,
+ test_visitor_in_native_list_int16);
input_visitor_test_add("/visitor/input/native_list/int32",
- &in_visitor_data,
- test_visitor_in_native_list_int32);
+ &in_visitor_data,
+ test_visitor_in_native_list_int32);
input_visitor_test_add("/visitor/input/native_list/int64",
- &in_visitor_data,
- test_visitor_in_native_list_int64);
+ &in_visitor_data,
+ test_visitor_in_native_list_int64);
input_visitor_test_add("/visitor/input/native_list/uint8",
- &in_visitor_data,
- test_visitor_in_native_list_uint8);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint8);
input_visitor_test_add("/visitor/input/native_list/uint16",
- &in_visitor_data,
- test_visitor_in_native_list_uint16);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint16);
input_visitor_test_add("/visitor/input/native_list/uint32",
- &in_visitor_data,
- test_visitor_in_native_list_uint32);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint32);
input_visitor_test_add("/visitor/input/native_list/uint64",
- &in_visitor_data, test_visitor_in_native_list_uint64);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint64);
input_visitor_test_add("/visitor/input/native_list/bool",
- &in_visitor_data, test_visitor_in_native_list_bool);
+ &in_visitor_data, test_visitor_in_native_list_bool);
input_visitor_test_add("/visitor/input/native_list/str",
- &in_visitor_data, test_visitor_in_native_list_string);
+ &in_visitor_data,
+ test_visitor_in_native_list_string);
input_visitor_test_add("/visitor/input/native_list/number",
- &in_visitor_data, test_visitor_in_native_list_number);
+ &in_visitor_data,
+ test_visitor_in_native_list_number);
g_test_run();
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 74020de5e7..f8c9367e48 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -1,7 +1,7 @@
/*
* QMP Output Visitor unit-tests.
*
- * Copyright (C) 2011 Red Hat Inc.
+ * Copyright (C) 2011, 2015 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
@@ -234,7 +234,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
{
int64_t value = 42;
Error *err = NULL;
- UserDefNested *ud2;
+ UserDefTwo *ud2;
QObject *obj;
QDict *qdict, *dict1, *dict2, *dict3, *userdef;
const char *string = "user def string";
@@ -244,21 +244,25 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
ud2 = g_malloc0(sizeof(*ud2));
ud2->string0 = g_strdup(strings[0]);
- ud2->dict1.string1 = g_strdup(strings[1]);
- ud2->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
- ud2->dict1.dict2.userdef1->string = g_strdup(string);
- ud2->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1);
- ud2->dict1.dict2.userdef1->base->integer = value;
- ud2->dict1.dict2.string2 = g_strdup(strings[2]);
-
- ud2->dict1.has_dict3 = true;
- ud2->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
- ud2->dict1.dict3.userdef2->string = g_strdup(string);
- ud2->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1);
- ud2->dict1.dict3.userdef2->base->integer = value;
- ud2->dict1.dict3.string3 = g_strdup(strings[3]);
-
- visit_type_UserDefNested(data->ov, &ud2, "unused", &err);
+ ud2->dict1 = g_malloc0(sizeof(*ud2->dict1));
+ ud2->dict1->string1 = g_strdup(strings[1]);
+
+ ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2));
+ ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1);
+ ud2->dict1->dict2->userdef->string = g_strdup(string);
+ ud2->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
+ ud2->dict1->dict2->userdef->base->integer = value;
+ ud2->dict1->dict2->string = g_strdup(strings[2]);
+
+ ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3));
+ ud2->dict1->has_dict3 = true;
+ ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1);
+ ud2->dict1->dict3->userdef->string = g_strdup(string);
+ ud2->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
+ ud2->dict1->dict3->userdef->base->integer = value;
+ ud2->dict1->dict3->string = g_strdup(strings[3]);
+
+ visit_type_UserDefTwo(data->ov, &ud2, "unused", &err);
g_assert(!err);
obj = qmp_output_get_qobject(data->qov);
@@ -275,22 +279,22 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
dict2 = qdict_get_qdict(dict1, "dict2");
g_assert_cmpint(qdict_size(dict2), ==, 2);
- g_assert_cmpstr(qdict_get_str(dict2, "string2"), ==, strings[2]);
- userdef = qdict_get_qdict(dict2, "userdef1");
+ g_assert_cmpstr(qdict_get_str(dict2, "string"), ==, strings[2]);
+ userdef = qdict_get_qdict(dict2, "userdef");
g_assert_cmpint(qdict_size(userdef), ==, 2);
g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
dict3 = qdict_get_qdict(dict1, "dict3");
g_assert_cmpint(qdict_size(dict3), ==, 2);
- g_assert_cmpstr(qdict_get_str(dict3, "string3"), ==, strings[3]);
- userdef = qdict_get_qdict(dict3, "userdef2");
+ g_assert_cmpstr(qdict_get_str(dict3, "string"), ==, strings[3]);
+ userdef = qdict_get_qdict(dict3, "userdef");
g_assert_cmpint(qdict_size(userdef), ==, 2);
g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
QDECREF(qdict);
- qapi_free_UserDefNested(ud2);
+ qapi_free_UserDefTwo(ud2);
}
static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
@@ -398,7 +402,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
const void *unused)
{
- UserDefNestedList *p, *head = NULL;
+ UserDefTwoList *p, *head = NULL;
const char string[] = "foo bar";
int i, max_count = 1024;
@@ -407,53 +411,21 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
p->value = g_malloc0(sizeof(*p->value));
p->value->string0 = g_strdup(string);
- p->value->dict1.string1 = g_strdup(string);
- p->value->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
- p->value->dict1.dict2.userdef1->string = g_strdup(string);
- p->value->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1);
- p->value->dict1.dict2.userdef1->base->integer = 42;
- p->value->dict1.dict2.string2 = g_strdup(string);
- p->value->dict1.has_dict3 = false;
+ p->value->dict1 = g_new0(UserDefTwoDict, 1);
+ p->value->dict1->string1 = g_strdup(string);
+ p->value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1);
+ p->value->dict1->dict2->userdef = g_new0(UserDefOne, 1);
+ p->value->dict1->dict2->userdef->string = g_strdup(string);
+ p->value->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
+ p->value->dict1->dict2->userdef->base->integer = 42;
+ p->value->dict1->dict2->string = g_strdup(string);
+ p->value->dict1->has_dict3 = false;
p->next = head;
head = p;
}
- qapi_free_UserDefNestedList(head);
-}
-
-static void test_visitor_out_union(TestOutputVisitorData *data,
- const void *unused)
-{
- QObject *arg, *qvalue;
- QDict *qdict, *value;
-
- Error *err = NULL;
-
- UserDefUnion *tmp = g_malloc0(sizeof(UserDefUnion));
- tmp->kind = USER_DEF_UNION_KIND_A;
- tmp->integer = 41;
- tmp->a = g_malloc0(sizeof(UserDefA));
- tmp->a->boolean = true;
-
- visit_type_UserDefUnion(data->ov, &tmp, NULL, &err);
- g_assert(err == NULL);
- arg = qmp_output_get_qobject(data->qov);
-
- g_assert(qobject_type(arg) == QTYPE_QDICT);
- qdict = qobject_to_qdict(arg);
-
- g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "a");
- g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41);
-
- qvalue = qdict_get(qdict, "data");
- g_assert(data != NULL);
- g_assert(qobject_type(qvalue) == QTYPE_QDICT);
- value = qobject_to_qdict(qvalue);
- g_assert_cmpint(qdict_get_bool(value, "boolean"), ==, true);
-
- qapi_free_UserDefUnion(tmp);
- QDECREF(qdict);
+ qapi_free_UserDefTwoList(head);
}
static void test_visitor_out_union_flat(TestOutputVisitorData *data,
@@ -487,24 +459,24 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
QDECREF(qdict);
}
-static void test_visitor_out_union_anon(TestOutputVisitorData *data,
- const void *unused)
+static void test_visitor_out_alternate(TestOutputVisitorData *data,
+ const void *unused)
{
QObject *arg;
Error *err = NULL;
- UserDefAnonUnion *tmp = g_malloc0(sizeof(UserDefAnonUnion));
- tmp->kind = USER_DEF_ANON_UNION_KIND_I;
+ UserDefAlternate *tmp = g_malloc0(sizeof(UserDefAlternate));
+ tmp->kind = USER_DEF_ALTERNATE_KIND_I;
tmp->i = 42;
- visit_type_UserDefAnonUnion(data->ov, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(data->ov, &tmp, NULL, &err);
g_assert(err == NULL);
arg = qmp_output_get_qobject(data->qov);
g_assert(qobject_type(arg) == QTYPE_QINT);
g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void test_visitor_out_empty(TestOutputVisitorData *data,
@@ -862,38 +834,48 @@ int main(int argc, char **argv)
&out_visitor_data, test_visitor_out_list);
output_visitor_test_add("/visitor/output/list-qapi-free",
&out_visitor_data, test_visitor_out_list_qapi_free);
- output_visitor_test_add("/visitor/output/union",
- &out_visitor_data, test_visitor_out_union);
output_visitor_test_add("/visitor/output/union-flat",
&out_visitor_data, test_visitor_out_union_flat);
- output_visitor_test_add("/visitor/output/union-anon",
- &out_visitor_data, test_visitor_out_union_anon);
+ output_visitor_test_add("/visitor/output/alternate",
+ &out_visitor_data, test_visitor_out_alternate);
output_visitor_test_add("/visitor/output/empty",
&out_visitor_data, test_visitor_out_empty);
output_visitor_test_add("/visitor/output/native_list/int",
- &out_visitor_data, test_visitor_out_native_list_int);
+ &out_visitor_data,
+ test_visitor_out_native_list_int);
output_visitor_test_add("/visitor/output/native_list/int8",
- &out_visitor_data, test_visitor_out_native_list_int8);
+ &out_visitor_data,
+ test_visitor_out_native_list_int8);
output_visitor_test_add("/visitor/output/native_list/int16",
- &out_visitor_data, test_visitor_out_native_list_int16);
+ &out_visitor_data,
+ test_visitor_out_native_list_int16);
output_visitor_test_add("/visitor/output/native_list/int32",
- &out_visitor_data, test_visitor_out_native_list_int32);
+ &out_visitor_data,
+ test_visitor_out_native_list_int32);
output_visitor_test_add("/visitor/output/native_list/int64",
- &out_visitor_data, test_visitor_out_native_list_int64);
+ &out_visitor_data,
+ test_visitor_out_native_list_int64);
output_visitor_test_add("/visitor/output/native_list/uint8",
- &out_visitor_data, test_visitor_out_native_list_uint8);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint8);
output_visitor_test_add("/visitor/output/native_list/uint16",
- &out_visitor_data, test_visitor_out_native_list_uint16);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint16);
output_visitor_test_add("/visitor/output/native_list/uint32",
- &out_visitor_data, test_visitor_out_native_list_uint32);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint32);
output_visitor_test_add("/visitor/output/native_list/uint64",
- &out_visitor_data, test_visitor_out_native_list_uint64);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint64);
output_visitor_test_add("/visitor/output/native_list/bool",
- &out_visitor_data, test_visitor_out_native_list_bool);
+ &out_visitor_data,
+ test_visitor_out_native_list_bool);
output_visitor_test_add("/visitor/output/native_list/string",
- &out_visitor_data, test_visitor_out_native_list_str);
+ &out_visitor_data,
+ test_visitor_out_native_list_str);
output_visitor_test_add("/visitor/output/native_list/number",
- &out_visitor_data, test_visitor_out_native_list_number);
+ &out_visitor_data,
+ test_visitor_out_native_list_number);
g_test_run();
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 7ad1886397..fa86cae88a 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1,6 +1,7 @@
/*
* Unit-tests for visitor-based serialization
*
+ * Copyright (C) 2014-2015 Red Hat, Inc.
* Copyright IBM, Corp. 2012
*
* Authors:
@@ -249,57 +250,62 @@ static void visit_struct(Visitor *v, void **native, Error **errp)
visit_type_TestStruct(v, (TestStruct **)native, NULL, errp);
}
-static UserDefNested *nested_struct_create(void)
+static UserDefTwo *nested_struct_create(void)
{
- UserDefNested *udnp = g_malloc0(sizeof(*udnp));
+ UserDefTwo *udnp = g_malloc0(sizeof(*udnp));
udnp->string0 = strdup("test_string0");
- udnp->dict1.string1 = strdup("test_string1");
- udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
- udnp->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1);
- udnp->dict1.dict2.userdef1->base->integer = 42;
- udnp->dict1.dict2.userdef1->string = strdup("test_string");
- udnp->dict1.dict2.string2 = strdup("test_string2");
- udnp->dict1.has_dict3 = true;
- udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
- udnp->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1);
- udnp->dict1.dict3.userdef2->base->integer = 43;
- udnp->dict1.dict3.userdef2->string = strdup("test_string");
- udnp->dict1.dict3.string3 = strdup("test_string3");
+ udnp->dict1 = g_malloc0(sizeof(*udnp->dict1));
+ udnp->dict1->string1 = strdup("test_string1");
+ udnp->dict1->dict2 = g_malloc0(sizeof(*udnp->dict1->dict2));
+ udnp->dict1->dict2->userdef = g_new0(UserDefOne, 1);
+ udnp->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
+ udnp->dict1->dict2->userdef->base->integer = 42;
+ udnp->dict1->dict2->userdef->string = strdup("test_string");
+ udnp->dict1->dict2->string = strdup("test_string2");
+ udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3));
+ udnp->dict1->has_dict3 = true;
+ udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1);
+ udnp->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
+ udnp->dict1->dict3->userdef->base->integer = 43;
+ udnp->dict1->dict3->userdef->string = strdup("test_string");
+ udnp->dict1->dict3->string = strdup("test_string3");
return udnp;
}
-static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2)
+static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2)
{
g_assert(udnp1);
g_assert(udnp2);
g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
- g_assert_cmpstr(udnp1->dict1.string1, ==, udnp2->dict1.string1);
- g_assert_cmpint(udnp1->dict1.dict2.userdef1->base->integer, ==,
- udnp2->dict1.dict2.userdef1->base->integer);
- g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==,
- udnp2->dict1.dict2.userdef1->string);
- g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2);
- g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3);
- g_assert_cmpint(udnp1->dict1.dict3.userdef2->base->integer, ==,
- udnp2->dict1.dict3.userdef2->base->integer);
- g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==,
- udnp2->dict1.dict3.userdef2->string);
- g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3);
+ g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1);
+ g_assert_cmpint(udnp1->dict1->dict2->userdef->base->integer, ==,
+ udnp2->dict1->dict2->userdef->base->integer);
+ g_assert_cmpstr(udnp1->dict1->dict2->userdef->string, ==,
+ udnp2->dict1->dict2->userdef->string);
+ g_assert_cmpstr(udnp1->dict1->dict2->string, ==,
+ udnp2->dict1->dict2->string);
+ g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3);
+ g_assert_cmpint(udnp1->dict1->dict3->userdef->base->integer, ==,
+ udnp2->dict1->dict3->userdef->base->integer);
+ g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==,
+ udnp2->dict1->dict3->userdef->string);
+ g_assert_cmpstr(udnp1->dict1->dict3->string, ==,
+ udnp2->dict1->dict3->string);
}
-static void nested_struct_cleanup(UserDefNested *udnp)
+static void nested_struct_cleanup(UserDefTwo *udnp)
{
- qapi_free_UserDefNested(udnp);
+ qapi_free_UserDefTwo(udnp);
}
static void visit_nested_struct(Visitor *v, void **native, Error **errp)
{
- visit_type_UserDefNested(v, (UserDefNested **)native, NULL, errp);
+ visit_type_UserDefTwo(v, (UserDefTwo **)native, NULL, errp);
}
static void visit_nested_struct_list(Visitor *v, void **native, Error **errp)
{
- visit_type_UserDefNestedList(v, (UserDefNestedList **)native, NULL, errp);
+ visit_type_UserDefTwoList(v, (UserDefTwoList **)native, NULL, errp);
}
/* test cases */
@@ -715,13 +721,14 @@ static void test_nested_struct(gconstpointer opaque)
{
TestArgs *args = (TestArgs *) opaque;
const SerializeOps *ops = args->ops;
- UserDefNested *udnp = nested_struct_create();
- UserDefNested *udnp_copy = NULL;
+ UserDefTwo *udnp = nested_struct_create();
+ UserDefTwo *udnp_copy = NULL;
Error *err = NULL;
void *serialize_data;
-
+
ops->serialize(udnp, &serialize_data, visit_nested_struct, &err);
- ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, &err);
+ ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct,
+ &err);
g_assert(err == NULL);
nested_struct_compare(udnp, udnp_copy);
@@ -737,18 +744,18 @@ static void test_nested_struct_list(gconstpointer opaque)
{
TestArgs *args = (TestArgs *) opaque;
const SerializeOps *ops = args->ops;
- UserDefNestedList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
+ UserDefTwoList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
Error *err = NULL;
void *serialize_data;
int i = 0;
for (i = 0; i < 8; i++) {
- tmp = g_malloc0(sizeof(UserDefNestedList));
+ tmp = g_new0(UserDefTwoList, 1);
tmp->value = nested_struct_create();
tmp->next = listp;
listp = tmp;
}
-
+
ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
ops->deserialize((void **)&listp_copy, serialize_data,
visit_nested_struct_list, &err);
@@ -764,8 +771,8 @@ static void test_nested_struct_list(gconstpointer opaque)
listp_copy = listp_copy->next;
}
- qapi_free_UserDefNestedList(tmp);
- qapi_free_UserDefNestedList(tmp_copy);
+ qapi_free_UserDefTwoList(tmp);
+ qapi_free_UserDefTwoList(tmp_copy);
ops->cleanup(serialize_data);
g_free(args);