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:
parent
d708cdbe87
commit
0d8b9fb5f2
|
@ -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 \
|
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 \
|
bad-type-dict.json double-data.json unknown-expr-key.json \
|
||||||
redefined-type.json redefined-command.json redefined-builtin.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 \
|
type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
|
||||||
missing-colon.json missing-comma-list.json \
|
data-array-empty.json data-array-unknown.json data-int.json \
|
||||||
missing-comma-object.json non-objects.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 \
|
qapi-schema-test.json quoted-structural-chars.json \
|
||||||
trailing-comma-list.json trailing-comma-object.json \
|
trailing-comma-list.json trailing-comma-object.json \
|
||||||
unclosed-list.json unclosed-object.json unclosed-string.json \
|
unclosed-list.json unclosed-object.json unclosed-string.json \
|
||||||
|
|
0
tests/qapi-schema/bad-data.err
Normal file
0
tests/qapi-schema/bad-data.err
Normal file
1
tests/qapi-schema/bad-data.exit
Normal file
1
tests/qapi-schema/bad-data.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/bad-data.json
Normal file
2
tests/qapi-schema/bad-data.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# FIXME: we should ensure 'data' is a dictionary for all but enums
|
||||||
|
{ 'command': 'oops', 'data': [ ] }
|
3
tests/qapi-schema/bad-data.out
Normal file
3
tests/qapi-schema/bad-data.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('data', [])])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/data-array-empty.err
Normal file
0
tests/qapi-schema/data-array-empty.err
Normal file
1
tests/qapi-schema/data-array-empty.exit
Normal file
1
tests/qapi-schema/data-array-empty.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/data-array-empty.json
Normal file
2
tests/qapi-schema/data-array-empty.json
Normal 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': [ ] } }
|
3
tests/qapi-schema/data-array-empty.out
Normal file
3
tests/qapi-schema/data-array-empty.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('empty', [])]))])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/data-array-unknown.err
Normal file
0
tests/qapi-schema/data-array-unknown.err
Normal file
1
tests/qapi-schema/data-array-unknown.exit
Normal file
1
tests/qapi-schema/data-array-unknown.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/data-array-unknown.json
Normal file
2
tests/qapi-schema/data-array-unknown.json
Normal 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' ] } }
|
3
tests/qapi-schema/data-array-unknown.out
Normal file
3
tests/qapi-schema/data-array-unknown.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('array', ['NoSuchType'])]))])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/data-int.err
Normal file
0
tests/qapi-schema/data-int.err
Normal file
1
tests/qapi-schema/data-int.exit
Normal file
1
tests/qapi-schema/data-int.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/data-int.json
Normal file
2
tests/qapi-schema/data-int.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# FIXME: we should reject commands where data is not an array or complex type
|
||||||
|
{ 'command': 'oops', 'data': 'int' }
|
3
tests/qapi-schema/data-int.out
Normal file
3
tests/qapi-schema/data-int.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('data', 'int')])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/data-member-array-bad.err
Normal file
0
tests/qapi-schema/data-member-array-bad.err
Normal file
1
tests/qapi-schema/data-member-array-bad.exit
Normal file
1
tests/qapi-schema/data-member-array-bad.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/data-member-array-bad.json
Normal file
2
tests/qapi-schema/data-member-array-bad.json
Normal 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' } ] } }
|
3
tests/qapi-schema/data-member-array-bad.out
Normal file
3
tests/qapi-schema/data-member-array-bad.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('member', [OrderedDict([('nested', 'str')])])]))])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/data-member-array.err
Normal file
0
tests/qapi-schema/data-member-array.err
Normal file
1
tests/qapi-schema/data-member-array.exit
Normal file
1
tests/qapi-schema/data-member-array.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
4
tests/qapi-schema/data-member-array.json
Normal file
4
tests/qapi-schema/data-member-array.json
Normal 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' ] } }
|
5
tests/qapi-schema/data-member-array.out
Normal file
5
tests/qapi-schema/data-member-array.out
Normal 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'])]))])]
|
0
tests/qapi-schema/data-member-unknown.err
Normal file
0
tests/qapi-schema/data-member-unknown.err
Normal file
1
tests/qapi-schema/data-member-unknown.exit
Normal file
1
tests/qapi-schema/data-member-unknown.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/data-member-unknown.json
Normal file
2
tests/qapi-schema/data-member-unknown.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# FIXME: we should reject data if it does not contain a known type
|
||||||
|
{ 'command': 'oops', 'data': { 'member': 'NoSuchType' } }
|
3
tests/qapi-schema/data-member-unknown.out
Normal file
3
tests/qapi-schema/data-member-unknown.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('data', OrderedDict([('member', 'NoSuchType')]))])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/data-unknown.err
Normal file
0
tests/qapi-schema/data-unknown.err
Normal file
1
tests/qapi-schema/data-unknown.exit
Normal file
1
tests/qapi-schema/data-unknown.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/data-unknown.json
Normal file
2
tests/qapi-schema/data-unknown.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# FIXME: we should reject data if it does not contain a known type
|
||||||
|
{ 'command': 'oops', 'data': 'NoSuchType' }
|
3
tests/qapi-schema/data-unknown.out
Normal file
3
tests/qapi-schema/data-unknown.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('data', 'NoSuchType')])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/nested-struct-data.err
Normal file
0
tests/qapi-schema/nested-struct-data.err
Normal file
1
tests/qapi-schema/nested-struct-data.exit
Normal file
1
tests/qapi-schema/nested-struct-data.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
4
tests/qapi-schema/nested-struct-data.json
Normal file
4
tests/qapi-schema/nested-struct-data.json
Normal 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': {} }
|
3
tests/qapi-schema/nested-struct-data.out
Normal file
3
tests/qapi-schema/nested-struct-data.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'foo'), ('data', OrderedDict([('a', OrderedDict([('string', 'str'), ('integer', 'int')])), ('b', 'str')])), ('returns', OrderedDict())])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/nested-struct-returns.err
Normal file
0
tests/qapi-schema/nested-struct-returns.err
Normal file
1
tests/qapi-schema/nested-struct-returns.exit
Normal file
1
tests/qapi-schema/nested-struct-returns.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
3
tests/qapi-schema/nested-struct-returns.json
Normal file
3
tests/qapi-schema/nested-struct-returns.json
Normal 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' } }
|
3
tests/qapi-schema/nested-struct-returns.out
Normal file
3
tests/qapi-schema/nested-struct-returns.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'foo'), ('returns', OrderedDict([('a', OrderedDict([('string', 'str'), ('integer', 'int')])), ('b', 'str')]))])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/returns-alternate.err
Normal file
0
tests/qapi-schema/returns-alternate.err
Normal file
1
tests/qapi-schema/returns-alternate.exit
Normal file
1
tests/qapi-schema/returns-alternate.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
3
tests/qapi-schema/returns-alternate.json
Normal file
3
tests/qapi-schema/returns-alternate.json
Normal 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' }
|
4
tests/qapi-schema/returns-alternate.out
Normal file
4
tests/qapi-schema/returns-alternate.out
Normal 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}]
|
||||||
|
[]
|
0
tests/qapi-schema/returns-array-bad.err
Normal file
0
tests/qapi-schema/returns-array-bad.err
Normal file
1
tests/qapi-schema/returns-array-bad.exit
Normal file
1
tests/qapi-schema/returns-array-bad.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/returns-array-bad.json
Normal file
2
tests/qapi-schema/returns-array-bad.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# FIXME: we should reject an array return that is not a single type
|
||||||
|
{ 'command': 'oops', 'returns': [ 'str', 'str' ] }
|
3
tests/qapi-schema/returns-array-bad.out
Normal file
3
tests/qapi-schema/returns-array-bad.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('returns', ['str', 'str'])])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/returns-int.err
Normal file
0
tests/qapi-schema/returns-int.err
Normal file
1
tests/qapi-schema/returns-int.exit
Normal file
1
tests/qapi-schema/returns-int.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/returns-int.json
Normal file
2
tests/qapi-schema/returns-int.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# It is okay (although not extensible) to return a non-dictionary
|
||||||
|
{ 'command': 'okay', 'returns': 'int' }
|
3
tests/qapi-schema/returns-int.out
Normal file
3
tests/qapi-schema/returns-int.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'okay'), ('returns', 'int')])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/returns-unknown.err
Normal file
0
tests/qapi-schema/returns-unknown.err
Normal file
1
tests/qapi-schema/returns-unknown.exit
Normal file
1
tests/qapi-schema/returns-unknown.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
2
tests/qapi-schema/returns-unknown.json
Normal file
2
tests/qapi-schema/returns-unknown.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# FIXME: we should reject returns if it does not contain a known type
|
||||||
|
{ 'command': 'oops', 'returns': 'NoSuchType' }
|
3
tests/qapi-schema/returns-unknown.out
Normal file
3
tests/qapi-schema/returns-unknown.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[OrderedDict([('command', 'oops'), ('returns', 'NoSuchType')])]
|
||||||
|
[]
|
||||||
|
[]
|
0
tests/qapi-schema/returns-whitelist.err
Normal file
0
tests/qapi-schema/returns-whitelist.err
Normal file
1
tests/qapi-schema/returns-whitelist.exit
Normal file
1
tests/qapi-schema/returns-whitelist.exit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
11
tests/qapi-schema/returns-whitelist.json
Normal file
11
tests/qapi-schema/returns-whitelist.json
Normal 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' ] }
|
7
tests/qapi-schema/returns-whitelist.out
Normal file
7
tests/qapi-schema/returns-whitelist.out
Normal 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']}]
|
||||||
|
[]
|
Loading…
Reference in a new issue