qapi: Add some type check tests

Demonstrate that the qapi generator silently parses confusing
types, which may cause other errors later on. Later patches
will update the expected results as the generator is made stricter.

Most of the new tests focus on blatant errors.  But
returns-whitelist is a case where we have historically allowed
returning something other than a JSON object from particular
commands; we have to keep that behavior to avoid breaking clients,
but it would be nicer to avoid adding such commands in the future,
because any return that is not an (array of) object cannot be
easily extended if future qemu wants to return additional
information.  The QMP protocol already documents that clients
should ignore unknown dictionary keys, but does not require
clients to have to handle more than one type of JSON object.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Eric Blake 2015-05-04 09:05:20 -06:00 committed by Markus Armbruster
parent d708cdbe87
commit 0d8b9fb5f2
61 changed files with 119 additions and 3 deletions

View file

@ -215,10 +215,14 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
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 event-max.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 \
missing-colon.json missing-comma-list.json \
missing-comma-object.json non-objects.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 \

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should ensure 'data' is a dictionary for all but enums
{ 'command': 'oops', 'data': [ ] }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('data', [])])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject an array for data if it does not contain a known type
{ 'command': 'oops', 'data': { 'empty': [ ] } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('empty', [])]))])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject an array for data if it does not contain a known type
{ 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('array', ['NoSuchType'])]))])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject commands where data is not an array or complex type
{ 'command': 'oops', 'data': 'int' }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('data', 'int')])]
[]
[]

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject data if it does not contain a valid array type
{ 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('member', [OrderedDict([('nested', 'str')])])]))])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,4 @@
# valid array members
{ 'enum': 'abc', 'data': [ 'a', 'b', 'c' ] }
{ 'type': 'def', 'data': { 'array': [ 'abc' ] } }
{ 'command': 'okay', 'data': { 'member1': [ 'int' ], 'member2': [ 'def' ] } }

View file

@ -0,0 +1,5 @@
[OrderedDict([('enum', 'abc'), ('data', ['a', 'b', 'c'])]),
OrderedDict([('type', 'def'), ('data', OrderedDict([('array', ['abc'])]))]),
OrderedDict([('command', 'okay'), ('data', OrderedDict([('member1', ['int']), ('member2', ['def'])]))])]
[{'enum_name': 'abc', 'enum_values': ['a', 'b', 'c']}]
[OrderedDict([('type', 'def'), ('data', OrderedDict([('array', ['abc'])]))])]

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject data if it does not contain a known type
{ 'command': 'oops', 'data': { 'member': 'NoSuchType' } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('member', 'NoSuchType')]))])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject data if it does not contain a known type
{ 'command': 'oops', 'data': 'NoSuchType' }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('data', 'NoSuchType')])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,4 @@
# FIXME: inline subtypes collide with our desired future use of defaults
{ 'command': 'foo',
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' },
'returns': {} }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'foo'), ('data', OrderedDict([('a', OrderedDict([('string', 'str'), ('integer', 'int')])), ('b', 'str')])), ('returns', OrderedDict())])]
[]
[]

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,3 @@
# FIXME: inline subtypes collide with our desired future use of defaults
{ 'command': 'foo',
'returns': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'foo'), ('returns', OrderedDict([('a', OrderedDict([('string', 'str'), ('integer', 'int')])), ('b', 'str')]))])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,3 @@
# FIXME: we should reject returns if it is an alternate type
{ 'alternate': 'Alt', 'data': { 'a': 'int', 'b': 'str' } }
{ 'command': 'oops', 'returns': 'Alt' }

View file

@ -0,0 +1,4 @@
[OrderedDict([('alternate', 'Alt'), ('data', OrderedDict([('a', 'int'), ('b', 'str')]))]),
OrderedDict([('command', 'oops'), ('returns', 'Alt')])]
[{'enum_name': 'AltKind', 'enum_values': None}]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject an array return that is not a single type
{ 'command': 'oops', 'returns': [ 'str', 'str' ] }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('returns', ['str', 'str'])])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# It is okay (although not extensible) to return a non-dictionary
{ 'command': 'okay', 'returns': 'int' }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'okay'), ('returns', 'int')])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: we should reject returns if it does not contain a known type
{ 'command': 'oops', 'returns': 'NoSuchType' }

View file

@ -0,0 +1,3 @@
[OrderedDict([('command', 'oops'), ('returns', 'NoSuchType')])]
[]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,11 @@
# FIXME: we should 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' ] }

View file

@ -0,0 +1,7 @@
[OrderedDict([('command', 'human-monitor-command'), ('data', OrderedDict([('command-line', 'str'), ('*cpu-index', 'int')])), ('returns', 'str')]),
OrderedDict([('enum', 'TpmModel'), ('data', ['tpm-tis'])]),
OrderedDict([('command', 'query-tpm-models'), ('returns', ['TpmModel'])]),
OrderedDict([('command', 'guest-get-time'), ('returns', 'int')]),
OrderedDict([('command', 'no-way-this-will-get-whitelisted'), ('returns', ['int'])])]
[{'enum_name': 'TpmModel', 'enum_values': ['tpm-tis']}]
[]