提交 3d0c4829 编写于 作者: E Eric Blake 提交者: Markus Armbruster

qapi: Add some union tests

Demonstrate that the qapi generator doesn't deal well with unions
that aren't up to par. Later patches will update the expected
reseults as the generator is made stricter.  A few tests work
as planned, but most show poor or missing error messages.

Of particular note, qapi-code-gen.txt documents 'base' only for
flat unions, but the tests here demonstrate that we currently allow
a 'base' to a simple union, although it is exercised only in the
testsuite.  Later patches will remove this undocumented feature, to
give us more flexibility in adding other future extensions to union
types.  For example, one possible extension is the idea of a
type-safe simple enum, where added fields tie the discriminator to
a user-defined enum type rather than creating an implicit enum from
the names in 'data'.  But adding such safety on top of a simple
enum with a base type could look ambiguous with a flat enum;
besides, the documentation also mentions how any simple union can
be represented by an equivalent flat union.  So it will be simpler
to just outlaw support for something we aren't using.
Signed-off-by: NEric Blake <eblake@redhat.com>
Reviewed-by: NMarkus Armbruster <armbru@redhat.com>
Signed-off-by: NMarkus Armbruster <armbru@redhat.com>
上级 cf393590
...@@ -217,10 +217,18 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ ...@@ -217,10 +217,18 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
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 \
duplicate-key.json union-invalid-base.json flat-union-no-base.json \ duplicate-key.json union-invalid-base.json union-bad-branch.json \
flat-union-invalid-discriminator.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-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-simple.json include-relpath.json include-format-err.json \
include-non-file.json include-no-file.json include-before-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-nested-err.json include-self-cycle.json include-cycle.json \
......
# FIXME: we should not allow array branches in anonymous unions
# TODO: should we support this?
{ 'type': 'One',
'data': { 'name': 'str' } }
{ 'union': 'MyUnion',
'discriminator': {},
'data': { 'one': 'One',
'two': [ 'int' ] } }
[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))]),
OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'One'), ('two', ['int'])]))])]
[{'enum_name': 'MyUnionKind', 'enum_values': None}]
[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))])]
# FIXME: we should reject anonymous union with base type
{ 'type': 'Base',
'data': { 'string': 'str' } }
{ 'union': 'MyUnion',
'base': 'Base',
'discriminator': {},
'data': { 'number': 'int' } }
[OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('union', 'MyUnion'), ('base', 'Base'), ('discriminator', OrderedDict()), ('data', OrderedDict([('number', 'int')]))])]
[{'enum_name': 'MyUnionKind', 'enum_values': None}]
[OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))])]
# FIXME: we should detect C enum collisions in an anonymous union
{ 'union': 'Union1',
'discriminator': {},
'data': { 'one': 'str', 'ONE': 'int' } }
[OrderedDict([('union', 'Union1'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'str'), ('ONE', 'int')]))])]
[{'enum_name': 'Union1Kind', 'enum_values': None}]
[]
# FIXME: we should reject anonymous unions with multiple object branches
{ 'type': 'One',
'data': { 'name': 'str' } }
{ 'type': 'Two',
'data': { 'value': 'int' } }
{ 'union': 'MyUnion',
'discriminator': {},
'data': { 'one': 'One',
'two': 'Two' } }
[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))]),
OrderedDict([('type', 'Two'), ('data', OrderedDict([('value', 'int')]))]),
OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'One'), ('two', 'Two')]))])]
[{'enum_name': 'MyUnionKind', 'enum_values': None}]
[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))]),
OrderedDict([('type', 'Two'), ('data', OrderedDict([('value', 'int')]))])]
# FIXME: we should reject anonymous unions with multiple string-like branches
{ 'enum': 'Enum',
'data': [ 'hello', 'world' ] }
{ 'union': 'MyUnion',
'discriminator': {},
'data': { 'one': 'str',
'two': 'Enum' } }
[OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'str'), ('two', 'Enum')]))])]
[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
{'enum_name': 'MyUnionKind', 'enum_values': None}]
[]
# Working example of anonymous union
{ 'type': 'Data',
'data': { '*number': 'int', '*name': 'str' } }
{ 'enum': 'Enum',
'data': [ 'hello', 'world' ] }
{ 'union': 'MyUnion',
'discriminator': {},
'data': { 'value': 'int',
'string': 'Enum',
'struct': 'Data' } }
[OrderedDict([('type', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]),
OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])]
[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
{'enum_name': 'MyUnionKind', 'enum_values': None}]
[OrderedDict([('type', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])]
# FIXME: we should reject a nested anonymous union branch
{ 'union': 'Union1',
'discriminator': {},
'data': { 'name': 'str', 'value': 'int' } }
{ 'union': 'Union2',
'discriminator': {},
'data': { 'nested': 'Union1' } }
[OrderedDict([('union', 'Union1'), ('discriminator', OrderedDict()), ('data', OrderedDict([('name', 'str'), ('value', 'int')]))]),
OrderedDict([('union', 'Union2'), ('discriminator', OrderedDict()), ('data', OrderedDict([('nested', 'Union1')]))])]
[{'enum_name': 'Union1Kind', 'enum_values': None},
{'enum_name': 'Union2Kind', 'enum_values': None}]
[]
# FIXME: we should reject an anonymous union with unknown type in branch
{ 'union': 'Union',
'discriminator': {},
'data': { 'unknown': 'MissingType' } }
[OrderedDict([('union', 'Union'), ('discriminator', OrderedDict()), ('data', OrderedDict([('unknown', 'MissingType')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]
tests/qapi-schema/flat-union-bad-base.json:9: Base 'OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')])' is not a valid type
# FIXME: poor message: we require the base to be an existing complex type
# TODO: should we allow an anonymous inline base type?
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'type': 'TestTypeA',
'data': { 'string': 'str' } }
{ 'type': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': { 'enum1': 'TestEnum', 'kind': 'str' },
'discriminator': 'enum1',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
# FIXME: we should require the discriminator to be a string naming a base-type member
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'type': 'TestBase',
'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
{ 'type': 'TestTypeA',
'data': { 'string': 'str' } }
{ 'type': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': 'TestBase',
'discriminator': [],
'data': { 'kind1': 'TestTypeA',
'kind2': 'TestTypeB' } }
[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')]))]),
OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
OrderedDict([('union', 'TestUnion'), ('base', 'TestBase'), ('discriminator', []), ('data', OrderedDict([('kind1', 'TestTypeA'), ('kind2', 'TestTypeB')]))])]
[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']},
{'enum_name': 'TestUnionKind', 'enum_values': None}]
[OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')]))]),
OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid type
# we require the base to be an existing complex type
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'type': 'TestTypeA',
'data': { 'string': 'str' } }
{ 'type': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': '**',
'discriminator': 'enum1',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
tests/qapi-schema/flat-union-base-union.json:11: Base 'UnionBase' is not a valid type
# FIXME: the error message needs help: we require the base to be a struct
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'type': 'TestTypeA',
'data': { 'string': 'str' } }
{ 'type': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'UnionBase',
'data': { 'kind1': 'TestTypeA',
'kind2': 'TestTypeB' } }
{ 'union': 'TestUnion',
'base': 'UnionBase',
'discriminator': 'type',
'data': { 'kind1': 'TestTypeA',
'kind2': 'TestTypeB' } }
# FIXME: we should check for no duplicate keys between branches and base
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'type': 'Base',
'data': { 'enum1': 'TestEnum', 'name': 'str' } }
{ 'type': 'Branch1',
'data': { 'name': 'str' } }
{ 'type': 'Branch2',
'data': { 'value': 'int' } }
{ 'union': 'TestUnion',
'base': 'Base',
'discriminator': 'enum1',
'data': { 'value1': 'Branch1',
'value2': 'Branch2' } }
[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]),
OrderedDict([('type', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]),
OrderedDict([('type', 'Branch2'), ('data', OrderedDict([('value', 'int')]))]),
OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'Branch1'), ('value2', 'Branch2')]))])]
[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
[OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]),
OrderedDict([('type', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]),
OrderedDict([('type', 'Branch2'), ('data', OrderedDict([('value', 'int')]))])]
tests/qapi-schema/flat-union-inline.json:7: Base 'OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')])' is not a valid type
# FIXME: poor message: we require branches to be a complex type name
# TODO: should we allow anonymous inline types?
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'type': 'Base',
'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
{ 'union': 'TestUnion',
'base': { 'enum1': 'TestEnum', 'kind': 'str' },
'discriminator': 'enum1',
'data': { 'value1': { 'string': 'str' },
'value2': { 'integer': 'int' } } }
# FIXME: we should require flat union branches to be a complex type
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'type': 'Base',
'data': { 'enum1': 'TestEnum' } }
{ 'type': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': 'Base',
'discriminator': 'enum1',
'data': { 'value1': 'int',
'value2': 'TestTypeB' } }
[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'int'), ('value2', 'TestTypeB')]))])]
[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
[OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
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 base field
# FIXME: flat unions should require a base
# TODO: simple unions should be able to use an enum discriminator
{ 'type': 'TestTypeA', { 'type': 'TestTypeA',
'data': { 'string': 'str' } } 'data': { 'string': 'str' } }
{ 'type': 'TestTypeB', { 'type': 'TestTypeB',
'data': { 'integer': 'int' } } 'data': { 'integer': 'int' } }
{ 'enum': 'Enum',
'data': [ 'value1', 'value2' ] }
{ 'union': 'TestUnion', { 'union': 'TestUnion',
'discriminator': 'enum1', 'discriminator': 'Enum',
'data': { 'value1': 'TestTypeA', 'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } } 'value2': 'TestTypeB' } }
# FIXME: we should require the discriminator to be non-optional
{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
{ 'type': 'Base',
'data': { '*switch': 'Enum' } }
{ 'type': 'Branch', 'data': { 'name': 'str' } }
{ 'union': 'MyUnion',
'base': 'Base',
'discriminator': '*switch',
'data': { 'one': 'Branch',
'two': 'Branch' } }
[OrderedDict([('enum', 'Enum'), ('data', ['one', 'two'])]),
OrderedDict([('type', 'Base'), ('data', OrderedDict([('*switch', 'Enum')]))]),
OrderedDict([('type', 'Branch'), ('data', OrderedDict([('name', 'str')]))]),
OrderedDict([('union', 'MyUnion'), ('base', 'Base'), ('discriminator', '*switch'), ('data', OrderedDict([('one', 'Branch'), ('two', 'Branch')]))])]
[{'enum_name': 'Enum', 'enum_values': ['one', 'two']}]
[OrderedDict([('type', 'Base'), ('data', OrderedDict([('*switch', 'Enum')]))]),
OrderedDict([('type', 'Branch'), ('data', OrderedDict([('name', 'str')]))])]
# FIXME: we should reject normal unions where branches would collide in C
{ 'type': 'One',
'data': { 'string': 'str' } }
{ 'type': 'Two',
'data': { 'number': 'int' } }
{ 'union': 'MyUnion',
'data': { 'one': 'One',
'ONE': 'Two' } }
[OrderedDict([('type', 'One'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('type', 'Two'), ('data', OrderedDict([('number', 'int')]))]),
OrderedDict([('union', 'MyUnion'), ('data', OrderedDict([('one', 'One'), ('ONE', 'Two')]))])]
[{'enum_name': 'MyUnionKind', 'enum_values': None}]
[OrderedDict([('type', 'One'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('type', 'Two'), ('data', OrderedDict([('number', 'int')]))])]
# FIXME: we should reject simple unions with a base
{ 'type': 'TestTypeA',
'data': { 'string': 'str' } }
{ 'type': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'type': 'Base',
'data': { 'string': 'str' } }
{ 'union': 'TestUnion',
'base': 'Base',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
[OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('data', OrderedDict([('value1', 'TestTypeA'), ('value2', 'TestTypeB')]))])]
[{'enum_name': 'TestUnionKind', 'enum_values': None}]
[OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))])]
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 type
# a union base type must be a struct
{ 'type': 'TestTypeA', { 'type': 'TestTypeA',
'data': { 'string': 'str' } } 'data': { 'string': 'str' } }
...@@ -5,6 +6,7 @@ ...@@ -5,6 +6,7 @@
'data': { 'integer': 'int' } } 'data': { 'integer': 'int' } }
{ 'union': 'TestUnion', { 'union': 'TestUnion',
'base': 'TestBaseWrong', 'base': 'int',
'discriminator': 'int',
'data': { 'value1': 'TestTypeA', 'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } } 'value2': 'TestTypeB' } }
# FIXME: we should reject 'max' branch in a union, for collision with C enum
{ 'union': 'Union',
'data': { 'max': 'int' } }
[OrderedDict([('union', 'Union'), ('data', OrderedDict([('max', 'int')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]
# FIXME: union branches cannot be optional
{ 'union': 'Union', 'data': { '*a': 'int', 'b': 'str' } }
[OrderedDict([('union', 'Union'), ('data', OrderedDict([('*a', 'int'), ('b', 'str')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]
# FIXME: we should reject a union with unknown type in branch
{ 'union': 'Union',
'data': { 'unknown': 'MissingType' } }
[OrderedDict([('union', 'Union'), ('data', OrderedDict([('unknown', 'MissingType')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册