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: 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:05 -06:00 committed by Markus Armbruster
parent cf3935907b
commit 3d0c482926
89 changed files with 325 additions and 9 deletions

View file

@ -217,10 +217,18 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
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 \

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,8 @@
# 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' ] } }

View file

@ -0,0 +1,4 @@
[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')]))])]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,7 @@
# FIXME: we should reject anonymous union with base type
{ 'type': 'Base',
'data': { 'string': 'str' } }
{ 'union': 'MyUnion',
'base': 'Base',
'discriminator': {},
'data': { 'number': 'int' } }

View file

@ -0,0 +1,4 @@
[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')]))])]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,4 @@
# FIXME: we should detect C enum collisions in an anonymous union
{ 'union': 'Union1',
'discriminator': {},
'data': { 'one': 'str', 'ONE': 'int' } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('union', 'Union1'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'str'), ('ONE', 'int')]))])]
[{'enum_name': 'Union1Kind', 'enum_values': None}]
[]

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,9 @@
# 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' } }

View file

@ -0,0 +1,6 @@
[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')]))])]

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,7 @@
# FIXME: we should reject anonymous unions with multiple string-like branches
{ 'enum': 'Enum',
'data': [ 'hello', 'world' ] }
{ 'union': 'MyUnion',
'discriminator': {},
'data': { 'one': 'str',
'two': 'Enum' } }

View file

@ -0,0 +1,5 @@
[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}]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,10 @@
# 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' } }

View file

@ -0,0 +1,6 @@
[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')]))])]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,7 @@
# FIXME: we should reject a nested anonymous union branch
{ 'union': 'Union1',
'discriminator': {},
'data': { 'name': 'str', 'value': 'int' } }
{ 'union': 'Union2',
'discriminator': {},
'data': { 'nested': 'Union1' } }

View file

@ -0,0 +1,5 @@
[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}]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,4 @@
# FIXME: we should reject an anonymous union with unknown type in branch
{ 'union': 'Union',
'discriminator': {},
'data': { 'unknown': 'MissingType' } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('union', 'Union'), ('discriminator', OrderedDict()), ('data', OrderedDict([('unknown', 'MissingType')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]

View file

@ -0,0 +1 @@
tests/qapi-schema/flat-union-bad-base.json:9: Base 'OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')])' is not a valid type

View file

@ -0,0 +1 @@
1

View file

@ -0,0 +1,13 @@
# 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' } }

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,14 @@
# 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' } }

View file

@ -0,0 +1,10 @@
[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')]))])]

View file

@ -0,0 +1 @@
tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid type

View file

@ -0,0 +1 @@
1

View file

@ -0,0 +1,12 @@
# 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' } }

View file

@ -0,0 +1 @@
tests/qapi-schema/flat-union-base-union.json:11: Base 'UnionBase' is not a valid type

View file

@ -0,0 +1 @@
1

View file

@ -0,0 +1,15 @@
# 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' } }

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,14 @@
# 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' } }

View file

@ -0,0 +1,9 @@
[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')]))])]

View file

@ -0,0 +1 @@
tests/qapi-schema/flat-union-inline.json:7: Base 'OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')])' is not a valid type

View file

@ -0,0 +1 @@
1

View file

@ -0,0 +1,11 @@
# 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' } } }

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,12 @@
# 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' } }

View file

@ -0,0 +1,7 @@
[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')]))])]

View file

@ -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 base field

View file

@ -1,10 +1,12 @@
# FIXME: flat unions should require a base
# TODO: simple unions should be able to use an enum discriminator
{ 'type': 'TestTypeA',
'data': { 'string': 'str' } }
{ 'type': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'enum': 'Enum',
'data': [ 'value1', 'value2' ] }
{ 'union': 'TestUnion',
'discriminator': 'enum1',
'discriminator': 'Enum',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,10 @@
# 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' } }

View file

@ -0,0 +1,7 @@
[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')]))])]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,8 @@
# 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' } }

View file

@ -0,0 +1,6 @@
[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')]))])]

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,14 @@
# 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' } }

View file

@ -0,0 +1,8 @@
[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')]))])]

View file

@ -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 type

View file

@ -1,3 +1,4 @@
# a union base type must be a struct
{ 'type': 'TestTypeA',
'data': { 'string': 'str' } }
@ -5,6 +6,7 @@
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': 'TestBaseWrong',
'base': 'int',
'discriminator': 'int',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,3 @@
# FIXME: we should reject 'max' branch in a union, for collision with C enum
{ 'union': 'Union',
'data': { 'max': 'int' } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('union', 'Union'), ('data', OrderedDict([('max', 'int')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,2 @@
# FIXME: union branches cannot be optional
{ 'union': 'Union', 'data': { '*a': 'int', 'b': 'str' } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('union', 'Union'), ('data', OrderedDict([('*a', 'int'), ('b', 'str')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]

View file

View file

@ -0,0 +1 @@
0

View file

@ -0,0 +1,3 @@
# FIXME: we should reject a union with unknown type in branch
{ 'union': 'Union',
'data': { 'unknown': 'MissingType' } }

View file

@ -0,0 +1,3 @@
[OrderedDict([('union', 'Union'), ('data', OrderedDict([('unknown', 'MissingType')]))])]
[{'enum_name': 'UnionKind', 'enum_values': None}]
[]