Expose some weaknesses in the generator: we don't always forbid the generation of structs that contain multiple members that map to the same C or QMP name. This has already been marked FIXME in qapi.py in commitd90675f
, but having more tests will make sure future patches produce desired behavior; and updating existing patches to better document things doesn't hurt, either. Some of these collisions are already caught in the old-style parser checks, but ultimately we want all collisions to be caught in the new-style QAPISchema*.check() methods. This patch focuses on C struct members, and does not consider collisions between commands and events (affecting C function names), or even collisions between generated C type names with user type names (for things like automatic FOOList struct representing array types or FOOKind for an implicit enum). There are two types of struct collisions we want to catch: 1) Collision between two keys in a JSON object. qapi.py prevents that within a single struct (see test duplicate-key), but it is possible to have collisions between a type's members and its base type's members (existing tests struct-base-clash, struct-base-clash-deep), and its flat union variant members (renamed test flat-union-clash-member). 2) Collision between two members of the C struct that is generated for a given QAPI type: a) Multiple QAPI names map to the same C name (new test args-name-clash) b) A QAPI name maps to a C name that is used for another purpose (new tests flat-union-clash-branch, struct-base-clash-base, union-clash-data). We already fixed some such cases in commit0f61af3e
and1e6c1616
, but more remain. c) Two C names generated for other purposes clash (updated test alternate-clash, new test union-clash-branches, union-clash-type, flat-union-clash-type) Ultimately, if we need to have a flat union where a tag value clashes with a base member name, we could change the generator to name the union (using 'foo.u.value' rather than 'foo.value') or otherwise munge the C name corresponding to tag values. But unless such a need arises, it will probably be easier to just forbid these collisions. Some of these negative tests will be deleted later, and positive tests added to qapi-schema-test.json in their place, when the generator code is reworked to avoid particular code generation collisions in class 2). [Note that viewing this patch with git rename detection enabled may see some confusion due to renaming some tests while adding others, but where the content is similar enough that git picks the wrong pre- and post-patch files to associate] Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1443565276-4535-6-git-send-email-eblake@redhat.com> [Improve commit message and comments a bit, drop an unrelated test] Signed-off-by: Markus Armbruster <armbru@redhat.com>
144 lines
4.9 KiB
Python
144 lines
4.9 KiB
Python
# *-*- Mode: Python -*-*
|
|
|
|
# for testing enums
|
|
{ 'enum': 'EnumOne',
|
|
'data': [ 'value1', 'value2', 'value3' ] }
|
|
{ 'struct': 'NestedEnumsOne',
|
|
'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
|
|
|
|
# for testing override of default naming heuristic
|
|
{ 'enum': 'QEnumTwo',
|
|
'prefix': 'QENUM_TWO',
|
|
'data': [ 'value1', 'value2' ] }
|
|
|
|
# for testing nested structs
|
|
{ 'struct': 'UserDefOne',
|
|
'base': 'UserDefZero', # intentional forward reference
|
|
'data': { 'string': 'str', '*enum1': 'EnumOne' } }
|
|
|
|
{ 'struct': 'UserDefZero',
|
|
'data': { 'integer': 'int' } }
|
|
|
|
{ 'struct': 'UserDefTwoDictDict',
|
|
'data': { 'userdef': 'UserDefOne', 'string': 'str' } }
|
|
|
|
{ 'struct': 'UserDefTwoDict',
|
|
'data': { 'string1': 'str',
|
|
'dict2': 'UserDefTwoDictDict',
|
|
'*dict3': 'UserDefTwoDictDict' } }
|
|
|
|
{ 'struct': 'UserDefTwo',
|
|
'data': { 'string0': 'str',
|
|
'dict1': 'UserDefTwoDict' } }
|
|
|
|
# for testing unions
|
|
# Among other things, test that a name collision between branches does
|
|
# not cause any problems (since only one branch can be in use at a time),
|
|
# by intentionally using two branches that both have a C member 'a_b'
|
|
{ 'struct': 'UserDefA',
|
|
'data': { 'boolean': 'bool', '*a_b': 'int' } }
|
|
|
|
{ 'struct': 'UserDefB',
|
|
'data': { 'intb': 'int', '*a-b': 'bool' } }
|
|
|
|
{ 'union': 'UserDefFlatUnion',
|
|
'base': 'UserDefUnionBase', # intentional forward reference
|
|
'discriminator': 'enum1',
|
|
'data': { 'value1' : 'UserDefA',
|
|
'value2' : 'UserDefB',
|
|
'value3' : 'UserDefB' } }
|
|
|
|
{ 'struct': 'UserDefUnionBase',
|
|
'base': 'UserDefZero',
|
|
'data': { 'string': 'str', 'enum1': 'EnumOne' } }
|
|
|
|
# this variant of UserDefFlatUnion defaults to a union that uses fields with
|
|
# allocated types to test corner cases in the cleanup/dealloc visitor
|
|
{ 'union': 'UserDefFlatUnion2',
|
|
'base': 'UserDefUnionBase',
|
|
'discriminator': 'enum1',
|
|
'data': { 'value1' : 'UserDefC', # intentional forward reference
|
|
'value2' : 'UserDefB',
|
|
'value3' : 'UserDefA' } }
|
|
|
|
{ 'alternate': 'UserDefAlternate',
|
|
'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } }
|
|
|
|
{ 'struct': 'UserDefC',
|
|
'data': { 'string1': 'str', 'string2': 'str' } }
|
|
|
|
# for testing native lists
|
|
{ 'union': 'UserDefNativeListUnion',
|
|
'data': { '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'],
|
|
'any': ['any'] } }
|
|
|
|
# testing commands
|
|
{ 'command': 'user_def_cmd', 'data': {} }
|
|
{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
|
|
{ 'command': 'user_def_cmd2',
|
|
'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
|
|
'returns': 'UserDefTwo' }
|
|
{ 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' },
|
|
'returns': 'int' }
|
|
# note: command name 'guest-sync' chosen to avoid "cannot use built-in" error
|
|
{ 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
|
|
|
|
# For testing integer range flattening in opts-visitor. The following schema
|
|
# corresponds to the option format:
|
|
#
|
|
# -userdef i64=3-6,i64=-5--1,u64=2,u16=1,u16=7-12
|
|
#
|
|
# For simplicity, this example doesn't use [type=]discriminator nor optargs
|
|
# specific to discriminator values.
|
|
{ 'struct': 'UserDefOptions',
|
|
'data': {
|
|
'*i64' : [ 'int' ],
|
|
'*u64' : [ 'uint64' ],
|
|
'*u16' : [ 'uint16' ],
|
|
'*i64x': 'int' ,
|
|
'*u64x': 'uint64' } }
|
|
|
|
# testing event
|
|
{ 'struct': 'EventStructOne',
|
|
'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
|
|
|
|
{ 'event': 'EVENT_A' }
|
|
{ 'event': 'EVENT_B',
|
|
'data': { } }
|
|
{ 'event': 'EVENT_C',
|
|
'data': { '*a': 'int', '*b': 'UserDefOne', 'c': 'str' } }
|
|
{ 'event': 'EVENT_D',
|
|
'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } }
|
|
|
|
# test that we correctly compile downstream extensions
|
|
{ 'enum': '__org.qemu_x-Enum', 'data': [ '__org.qemu_x-value' ] }
|
|
{ 'struct': '__org.qemu_x-Base',
|
|
'data': { '__org.qemu_x-member1': '__org.qemu_x-Enum' } }
|
|
{ 'struct': '__org.qemu_x-Struct', 'base': '__org.qemu_x-Base',
|
|
'data': { '__org.qemu_x-member2': 'str' } }
|
|
{ 'union': '__org.qemu_x-Union1', 'data': { '__org.qemu_x-branch': 'str' } }
|
|
{ 'struct': '__org.qemu_x-Struct2',
|
|
'data': { 'array': ['__org.qemu_x-Union1'] } }
|
|
{ 'union': '__org.qemu_x-Union2', 'base': '__org.qemu_x-Base',
|
|
'discriminator': '__org.qemu_x-member1',
|
|
'data': { '__org.qemu_x-value': '__org.qemu_x-Struct2' } }
|
|
{ 'alternate': '__org.qemu_x-Alt',
|
|
'data': { '__org.qemu_x-branch': 'str', 'b': '__org.qemu_x-Base' } }
|
|
{ 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' }
|
|
{ 'command': '__org.qemu_x-command',
|
|
'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
|
|
'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
|
|
'returns': '__org.qemu_x-Union1' }
|