mirror of https://github.com/xemu-project/xemu.git
QAPI patches patches for 2023-04-26
-----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmRIvOkSHGFybWJydUBy ZWRoYXQuY29tAAoJEDhwtADrkYZTX3MQAIqrKQbOzQ81/cDZ7aeLOroDoGYf1Cs0 0NiEVlyoblWNzL3HraGgXiNRTP+zaG/TcFKza1nz8qjdkxWxBjdbfF5Lm6mQf5Zo tcHUjksmnUlPkLYSOyEjfY9SNvcS6g7djE/NF5lbJtzYGZScZpLarELR7oUvrcXR AEiw8N5FZXp+j6cTeWvrLzSqz9qBsFJUCGcGER0T/Mt5MlUwDexE1xe7g8oD5l+b s0jeQr1PTZm5k6ehajQtgbHvAkgH8xVTKqbB/U5iz4VhYriH+IPEOtfCFt6/1soz pVkYikJpazCvQMjqnWu9dE1onthgSsEIOV29kFU0Kr8ATZuJBQMuLVp4hSsbKANj BUVyL2/fUsIp7gd+KikXUOjKYajxek6Q2YLAPpL+1pBCTql/PBQ7td8CECdiv/9e Xh50q+BGvyEiyoyf4EEpaLXUZog605WHEaODj9uPtNHJP9x6Rqt93FUsdWUtt/k9 hJ8RSKy8njr0vxGoJkj89m2XfCwtuX3VQ5IXvv/If4U5Y4+JhcLtiqW+Njh8fAM4 ZwIrlUYG7inLUKFVcQ3sEGpaj611i5ixIxctUvEiggZX+fPeSFKYUr+Rq8WXM8gv suLXz7VF6H4Sw30lCvdQ4LSungbzlYAtQYpmdEQGoM8iasIi4PoDf0cTYBbMYHDX +pZvWC50cVtf =wLx6 -----END PGP SIGNATURE----- Merge tag 'pull-qapi-2023-04-26' of https://repo.or.cz/qemu/armbru into staging QAPI patches patches for 2023-04-26 # -----BEGIN PGP SIGNATURE----- # # iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmRIvOkSHGFybWJydUBy # ZWRoYXQuY29tAAoJEDhwtADrkYZTX3MQAIqrKQbOzQ81/cDZ7aeLOroDoGYf1Cs0 # 0NiEVlyoblWNzL3HraGgXiNRTP+zaG/TcFKza1nz8qjdkxWxBjdbfF5Lm6mQf5Zo # tcHUjksmnUlPkLYSOyEjfY9SNvcS6g7djE/NF5lbJtzYGZScZpLarELR7oUvrcXR # AEiw8N5FZXp+j6cTeWvrLzSqz9qBsFJUCGcGER0T/Mt5MlUwDexE1xe7g8oD5l+b # s0jeQr1PTZm5k6ehajQtgbHvAkgH8xVTKqbB/U5iz4VhYriH+IPEOtfCFt6/1soz # pVkYikJpazCvQMjqnWu9dE1onthgSsEIOV29kFU0Kr8ATZuJBQMuLVp4hSsbKANj # BUVyL2/fUsIp7gd+KikXUOjKYajxek6Q2YLAPpL+1pBCTql/PBQ7td8CECdiv/9e # Xh50q+BGvyEiyoyf4EEpaLXUZog605WHEaODj9uPtNHJP9x6Rqt93FUsdWUtt/k9 # hJ8RSKy8njr0vxGoJkj89m2XfCwtuX3VQ5IXvv/If4U5Y4+JhcLtiqW+Njh8fAM4 # ZwIrlUYG7inLUKFVcQ3sEGpaj611i5ixIxctUvEiggZX+fPeSFKYUr+Rq8WXM8gv # suLXz7VF6H4Sw30lCvdQ4LSungbzlYAtQYpmdEQGoM8iasIi4PoDf0cTYBbMYHDX # +pZvWC50cVtf # =wLx6 # -----END PGP SIGNATURE----- # gpg: Signature made Wed 26 Apr 2023 06:55:53 AM BST # gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653 # gpg: issuer "armbru@redhat.com" # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [undefined] # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * tag 'pull-qapi-2023-04-26' of https://repo.or.cz/qemu/armbru: qapi: allow unions to contain further unions qapi: Improve specificity of type/member descriptions qapi: support updating expected test output via make qapi: Require boxed for conditional command and event arguments qapi: Fix code generated for optional conditional struct member tests/qapi-schema: Cover optional conditional struct member tests/qapi-schema: Clean up positive test for conditionals tests/qapi-schema: Rename a few conditionals tests/qapi-schema: Improve union discriminator coverage qapi: Fix to reject 'data': 'mumble' in struct qapi: Fix error message when type name or array is expected qapi: Simplify code a bit after previous commits qapi: Improve error message for unexpected array types qapi: Split up check_type() qapi: Clean up after removal of simple unions qapi/schema: Use super() qapi: Fix error message format regression Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
c3f9aa8e48
|
@ -805,9 +805,8 @@ gets its generated code guarded like this::
|
||||||
... generated code ...
|
... generated code ...
|
||||||
#endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */
|
#endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */
|
||||||
|
|
||||||
Individual members of complex types, commands arguments, and
|
Individual members of complex types can also be made conditional.
|
||||||
event-specific data can also be made conditional. This requires the
|
This requires the longhand form of MEMBER.
|
||||||
longhand form of MEMBER.
|
|
||||||
|
|
||||||
Example: a struct type with unconditional member 'foo' and conditional
|
Example: a struct type with unconditional member 'foo' and conditional
|
||||||
member 'bar' ::
|
member 'bar' ::
|
||||||
|
|
|
@ -66,6 +66,7 @@ def gen_call(name: str,
|
||||||
elif arg_type:
|
elif arg_type:
|
||||||
assert not arg_type.variants
|
assert not arg_type.variants
|
||||||
for memb in arg_type.members:
|
for memb in arg_type.members:
|
||||||
|
assert not memb.ifcond.is_present()
|
||||||
if memb.need_has():
|
if memb.need_has():
|
||||||
argstr += 'arg.has_%s, ' % c_name(memb.name)
|
argstr += 'arg.has_%s, ' % c_name(memb.name)
|
||||||
argstr += 'arg.%s, ' % c_name(memb.name)
|
argstr += 'arg.%s, ' % c_name(memb.name)
|
||||||
|
|
|
@ -333,62 +333,56 @@ def normalize_members(members: object) -> None:
|
||||||
members[key] = {'type': arg}
|
members[key] = {'type': arg}
|
||||||
|
|
||||||
|
|
||||||
def check_type(value: Optional[object],
|
def check_type_name(value: Optional[object],
|
||||||
info: QAPISourceInfo,
|
info: QAPISourceInfo, source: str) -> None:
|
||||||
source: str,
|
if value is not None and not isinstance(value, str):
|
||||||
allow_array: bool = False,
|
raise QAPISemError(info, "%s should be a type name" % source)
|
||||||
allow_dict: Union[bool, str] = False) -> None:
|
|
||||||
"""
|
|
||||||
Normalize and validate the QAPI type of ``value``.
|
|
||||||
|
|
||||||
Python types of ``str`` or ``None`` are always allowed.
|
|
||||||
|
def check_type_name_or_array(value: Optional[object],
|
||||||
|
info: QAPISourceInfo, source: str) -> None:
|
||||||
|
if value is None or isinstance(value, str):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not isinstance(value, list):
|
||||||
|
raise QAPISemError(info,
|
||||||
|
"%s should be a type name or array" % source)
|
||||||
|
|
||||||
|
if len(value) != 1 or not isinstance(value[0], str):
|
||||||
|
raise QAPISemError(info,
|
||||||
|
"%s: array type must contain single type name" %
|
||||||
|
source)
|
||||||
|
|
||||||
|
|
||||||
|
def check_type_implicit(value: Optional[object],
|
||||||
|
info: QAPISourceInfo, source: str,
|
||||||
|
parent_name: Optional[str]) -> None:
|
||||||
|
"""
|
||||||
|
Normalize and validate an optional implicit struct type.
|
||||||
|
|
||||||
|
Accept ``None`` or a ``dict`` defining an implicit struct type.
|
||||||
|
The latter is normalized in place.
|
||||||
|
|
||||||
:param value: The value to check.
|
:param value: The value to check.
|
||||||
:param info: QAPI schema source file information.
|
:param info: QAPI schema source file information.
|
||||||
:param source: Error string describing this ``value``.
|
:param source: Error string describing this ``value``.
|
||||||
:param allow_array:
|
:param parent_name:
|
||||||
Allow a ``List[str]`` of length 1, which indicates an array of
|
When the value of ``parent_name`` is in pragma
|
||||||
the type named by the list element.
|
``member-name-exceptions``, an implicit struct type may
|
||||||
:param allow_dict:
|
violate the member naming rules.
|
||||||
Allow a dict. Its members can be struct type members or union
|
|
||||||
branches. When the value of ``allow_dict`` is in pragma
|
|
||||||
``member-name-exceptions``, the dict's keys may violate the
|
|
||||||
member naming rules. The dict members are normalized in place.
|
|
||||||
|
|
||||||
:raise QAPISemError: When ``value`` fails validation.
|
:raise QAPISemError: When ``value`` fails validation.
|
||||||
:return: None, ``value`` is normalized in-place as needed.
|
:return: None
|
||||||
"""
|
"""
|
||||||
if value is None:
|
if value is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Type name
|
|
||||||
if isinstance(value, str):
|
|
||||||
return
|
|
||||||
|
|
||||||
# Array type
|
|
||||||
if isinstance(value, list):
|
|
||||||
if not allow_array:
|
|
||||||
raise QAPISemError(info, "%s cannot be an array" % source)
|
|
||||||
if len(value) != 1 or not isinstance(value[0], str):
|
|
||||||
raise QAPISemError(info,
|
|
||||||
"%s: array type must contain single type name" %
|
|
||||||
source)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Anonymous type
|
|
||||||
|
|
||||||
if not allow_dict:
|
|
||||||
raise QAPISemError(info, "%s should be a type name" % source)
|
|
||||||
|
|
||||||
if not isinstance(value, dict):
|
if not isinstance(value, dict):
|
||||||
raise QAPISemError(info,
|
raise QAPISemError(info,
|
||||||
"%s should be an object or type name" % source)
|
"%s should be an object or type name" % source)
|
||||||
|
|
||||||
permissive = False
|
permissive = parent_name in info.pragma.member_name_exceptions
|
||||||
if isinstance(allow_dict, str):
|
|
||||||
permissive = allow_dict in info.pragma.member_name_exceptions
|
|
||||||
|
|
||||||
# value is a dictionary, check that each member is okay
|
|
||||||
for (key, arg) in value.items():
|
for (key, arg) in value.items():
|
||||||
key_source = "%s member '%s'" % (source, key)
|
key_source = "%s member '%s'" % (source, key)
|
||||||
if key.startswith('*'):
|
if key.startswith('*'):
|
||||||
|
@ -401,7 +395,16 @@ def check_type(value: Optional[object],
|
||||||
check_keys(arg, info, key_source, ['type'], ['if', 'features'])
|
check_keys(arg, info, key_source, ['type'], ['if', 'features'])
|
||||||
check_if(arg, info, key_source)
|
check_if(arg, info, key_source)
|
||||||
check_features(arg.get('features'), info)
|
check_features(arg.get('features'), info)
|
||||||
check_type(arg['type'], info, key_source, allow_array=True)
|
check_type_name_or_array(arg['type'], info, key_source)
|
||||||
|
|
||||||
|
|
||||||
|
def check_type_name_or_implicit(value: Optional[object],
|
||||||
|
info: QAPISourceInfo, source: str,
|
||||||
|
parent_name: Optional[str]) -> None:
|
||||||
|
if value is None or isinstance(value, str):
|
||||||
|
return
|
||||||
|
|
||||||
|
check_type_implicit(value, info, source, parent_name)
|
||||||
|
|
||||||
|
|
||||||
def check_features(features: Optional[object],
|
def check_features(features: Optional[object],
|
||||||
|
@ -489,8 +492,8 @@ def check_struct(expr: QAPIExpression) -> None:
|
||||||
name = cast(str, expr['struct']) # Checked in check_exprs
|
name = cast(str, expr['struct']) # Checked in check_exprs
|
||||||
members = expr['data']
|
members = expr['data']
|
||||||
|
|
||||||
check_type(members, expr.info, "'data'", allow_dict=name)
|
check_type_implicit(members, expr.info, "'data'", name)
|
||||||
check_type(expr.get('base'), expr.info, "'base'")
|
check_type_name(expr.get('base'), expr.info, "'base'")
|
||||||
|
|
||||||
|
|
||||||
def check_union(expr: QAPIExpression) -> None:
|
def check_union(expr: QAPIExpression) -> None:
|
||||||
|
@ -508,7 +511,7 @@ def check_union(expr: QAPIExpression) -> None:
|
||||||
members = expr['data']
|
members = expr['data']
|
||||||
info = expr.info
|
info = expr.info
|
||||||
|
|
||||||
check_type(base, info, "'base'", allow_dict=name)
|
check_type_name_or_implicit(base, info, "'base'", name)
|
||||||
check_name_is_str(discriminator, info, "'discriminator'")
|
check_name_is_str(discriminator, info, "'discriminator'")
|
||||||
|
|
||||||
if not isinstance(members, dict):
|
if not isinstance(members, dict):
|
||||||
|
@ -518,7 +521,7 @@ def check_union(expr: QAPIExpression) -> None:
|
||||||
source = "'data' member '%s'" % key
|
source = "'data' member '%s'" % key
|
||||||
check_keys(value, info, source, ['type'], ['if'])
|
check_keys(value, info, source, ['type'], ['if'])
|
||||||
check_if(value, info, source)
|
check_if(value, info, source)
|
||||||
check_type(value['type'], info, source, allow_array=not base)
|
check_type_name(value['type'], info, source)
|
||||||
|
|
||||||
|
|
||||||
def check_alternate(expr: QAPIExpression) -> None:
|
def check_alternate(expr: QAPIExpression) -> None:
|
||||||
|
@ -544,7 +547,7 @@ def check_alternate(expr: QAPIExpression) -> None:
|
||||||
check_name_lower(key, info, source)
|
check_name_lower(key, info, source)
|
||||||
check_keys(value, info, source, ['type'], ['if'])
|
check_keys(value, info, source, ['type'], ['if'])
|
||||||
check_if(value, info, source)
|
check_if(value, info, source)
|
||||||
check_type(value['type'], info, source, allow_array=True)
|
check_type_name_or_array(value['type'], info, source)
|
||||||
|
|
||||||
|
|
||||||
def check_command(expr: QAPIExpression) -> None:
|
def check_command(expr: QAPIExpression) -> None:
|
||||||
|
@ -560,10 +563,13 @@ def check_command(expr: QAPIExpression) -> None:
|
||||||
rets = expr.get('returns')
|
rets = expr.get('returns')
|
||||||
boxed = expr.get('boxed', False)
|
boxed = expr.get('boxed', False)
|
||||||
|
|
||||||
if boxed and args is None:
|
if boxed:
|
||||||
raise QAPISemError(expr.info, "'boxed': true requires 'data'")
|
if args is None:
|
||||||
check_type(args, expr.info, "'data'", allow_dict=not boxed)
|
raise QAPISemError(expr.info, "'boxed': true requires 'data'")
|
||||||
check_type(rets, expr.info, "'returns'", allow_array=True)
|
check_type_name(args, expr.info, "'data'")
|
||||||
|
else:
|
||||||
|
check_type_name_or_implicit(args, expr.info, "'data'", None)
|
||||||
|
check_type_name_or_array(rets, expr.info, "'returns'")
|
||||||
|
|
||||||
|
|
||||||
def check_event(expr: QAPIExpression) -> None:
|
def check_event(expr: QAPIExpression) -> None:
|
||||||
|
@ -578,9 +584,12 @@ def check_event(expr: QAPIExpression) -> None:
|
||||||
args = expr.get('data')
|
args = expr.get('data')
|
||||||
boxed = expr.get('boxed', False)
|
boxed = expr.get('boxed', False)
|
||||||
|
|
||||||
if boxed and args is None:
|
if boxed:
|
||||||
raise QAPISemError(expr.info, "'boxed': true requires 'data'")
|
if args is None:
|
||||||
check_type(args, expr.info, "'data'", allow_dict=not boxed)
|
raise QAPISemError(expr.info, "'boxed': true requires 'data'")
|
||||||
|
check_type_name(args, expr.info, "'data'")
|
||||||
|
else:
|
||||||
|
check_type_name_or_implicit(args, expr.info, "'data'", None)
|
||||||
|
|
||||||
|
|
||||||
def check_exprs(exprs: List[QAPIExpression]) -> List[QAPIExpression]:
|
def check_exprs(exprs: List[QAPIExpression]) -> List[QAPIExpression]:
|
||||||
|
|
|
@ -119,6 +119,7 @@ def build_params(arg_type: Optional[QAPISchemaObjectType],
|
||||||
elif arg_type:
|
elif arg_type:
|
||||||
assert not arg_type.variants
|
assert not arg_type.variants
|
||||||
for memb in arg_type.members:
|
for memb in arg_type.members:
|
||||||
|
assert not memb.ifcond.is_present()
|
||||||
ret += sep
|
ret += sep
|
||||||
sep = ', '
|
sep = ', '
|
||||||
if memb.need_has():
|
if memb.need_has():
|
||||||
|
|
|
@ -98,6 +98,6 @@ def main() -> int:
|
||||||
builtins=args.builtins,
|
builtins=args.builtins,
|
||||||
gen_tracing=not args.suppress_tracing)
|
gen_tracing=not args.suppress_tracing)
|
||||||
except QAPIError as err:
|
except QAPIError as err:
|
||||||
print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr)
|
print(err, file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -259,7 +259,7 @@ class QAPISchemaType(QAPISchemaEntity):
|
||||||
return not self.c_type().endswith(POINTER_SUFFIX)
|
return not self.c_type().endswith(POINTER_SUFFIX)
|
||||||
|
|
||||||
def check(self, schema):
|
def check(self, schema):
|
||||||
QAPISchemaEntity.check(self, schema)
|
super().check(schema)
|
||||||
for feat in self.features:
|
for feat in self.features:
|
||||||
if feat.is_special():
|
if feat.is_special():
|
||||||
raise QAPISemError(
|
raise QAPISemError(
|
||||||
|
@ -465,9 +465,10 @@ class QAPISchemaObjectType(QAPISchemaType):
|
||||||
# on behalf of info, which is not necessarily self.info
|
# on behalf of info, which is not necessarily self.info
|
||||||
def check_clash(self, info, seen):
|
def check_clash(self, info, seen):
|
||||||
assert self._checked
|
assert self._checked
|
||||||
assert not self.variants # not implemented
|
|
||||||
for m in self.members:
|
for m in self.members:
|
||||||
m.check_clash(info, seen)
|
m.check_clash(info, seen)
|
||||||
|
if self.variants:
|
||||||
|
self.variants.check_clash(info, seen)
|
||||||
|
|
||||||
def connect_doc(self, doc=None):
|
def connect_doc(self, doc=None):
|
||||||
super().connect_doc(doc)
|
super().connect_doc(doc)
|
||||||
|
@ -486,6 +487,10 @@ class QAPISchemaObjectType(QAPISchemaType):
|
||||||
assert self.members is not None
|
assert self.members is not None
|
||||||
return not self.members and not self.variants
|
return not self.members and not self.variants
|
||||||
|
|
||||||
|
def has_conditional_members(self):
|
||||||
|
assert self.members is not None
|
||||||
|
return any(m.ifcond.is_present() for m in self.members)
|
||||||
|
|
||||||
def c_name(self):
|
def c_name(self):
|
||||||
assert self.name != 'q_empty'
|
assert self.name != 'q_empty'
|
||||||
return super().c_name()
|
return super().c_name()
|
||||||
|
@ -652,8 +657,7 @@ class QAPISchemaVariants:
|
||||||
self.info,
|
self.info,
|
||||||
"branch '%s' is not a value of %s"
|
"branch '%s' is not a value of %s"
|
||||||
% (v.name, self.tag_member.type.describe()))
|
% (v.name, self.tag_member.type.describe()))
|
||||||
if (not isinstance(v.type, QAPISchemaObjectType)
|
if not isinstance(v.type, QAPISchemaObjectType):
|
||||||
or v.type.variants):
|
|
||||||
raise QAPISemError(
|
raise QAPISemError(
|
||||||
self.info,
|
self.info,
|
||||||
"%s cannot use %s"
|
"%s cannot use %s"
|
||||||
|
@ -697,6 +701,7 @@ class QAPISchemaMember:
|
||||||
|
|
||||||
def describe(self, info):
|
def describe(self, info):
|
||||||
role = self.role
|
role = self.role
|
||||||
|
meta = 'type'
|
||||||
defined_in = self.defined_in
|
defined_in = self.defined_in
|
||||||
assert defined_in
|
assert defined_in
|
||||||
|
|
||||||
|
@ -708,13 +713,17 @@ class QAPISchemaMember:
|
||||||
# Implicit type created for a command's dict 'data'
|
# Implicit type created for a command's dict 'data'
|
||||||
assert role == 'member'
|
assert role == 'member'
|
||||||
role = 'parameter'
|
role = 'parameter'
|
||||||
|
meta = 'command'
|
||||||
|
defined_in = defined_in[:-4]
|
||||||
elif defined_in.endswith('-base'):
|
elif defined_in.endswith('-base'):
|
||||||
# Implicit type created for a union's dict 'base'
|
# Implicit type created for a union's dict 'base'
|
||||||
role = 'base ' + role
|
role = 'base ' + role
|
||||||
|
defined_in = defined_in[:-5]
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
elif defined_in != info.defn_name:
|
|
||||||
return "%s '%s' of type '%s'" % (role, self.name, defined_in)
|
if defined_in != info.defn_name:
|
||||||
|
return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
|
||||||
return "%s '%s'" % (role, self.name)
|
return "%s '%s'" % (role, self.name)
|
||||||
|
|
||||||
|
|
||||||
|
@ -817,6 +826,11 @@ class QAPISchemaCommand(QAPISchemaEntity):
|
||||||
self.info,
|
self.info,
|
||||||
"command's 'data' can take %s only with 'boxed': true"
|
"command's 'data' can take %s only with 'boxed': true"
|
||||||
% self.arg_type.describe())
|
% self.arg_type.describe())
|
||||||
|
self.arg_type.check(schema)
|
||||||
|
if self.arg_type.has_conditional_members() and not self.boxed:
|
||||||
|
raise QAPISemError(
|
||||||
|
self.info,
|
||||||
|
"conditional command arguments require 'boxed': true")
|
||||||
if self._ret_type_name:
|
if self._ret_type_name:
|
||||||
self.ret_type = schema.resolve_type(
|
self.ret_type = schema.resolve_type(
|
||||||
self._ret_type_name, self.info, "command's 'returns'")
|
self._ret_type_name, self.info, "command's 'returns'")
|
||||||
|
@ -872,6 +886,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
|
||||||
self.info,
|
self.info,
|
||||||
"event's 'data' can take %s only with 'boxed': true"
|
"event's 'data' can take %s only with 'boxed': true"
|
||||||
% self.arg_type.describe())
|
% self.arg_type.describe())
|
||||||
|
self.arg_type.check(schema)
|
||||||
|
if self.arg_type.has_conditional_members() and not self.boxed:
|
||||||
|
raise QAPISemError(
|
||||||
|
self.info,
|
||||||
|
"conditional event arguments require 'boxed': true")
|
||||||
|
|
||||||
def connect_doc(self, doc=None):
|
def connect_doc(self, doc=None):
|
||||||
super().connect_doc(doc)
|
super().connect_doc(doc)
|
||||||
|
|
|
@ -74,11 +74,13 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
sep = ''
|
sep = ''
|
||||||
for memb in members:
|
for memb in members:
|
||||||
if memb.optional and not memb.need_has():
|
if memb.optional and not memb.need_has():
|
||||||
|
ret += memb.ifcond.gen_if()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
bool has_%(c_name)s = !!obj->%(c_name)s;
|
bool has_%(c_name)s = !!obj->%(c_name)s;
|
||||||
''',
|
''',
|
||||||
c_name=c_name(memb.name))
|
c_name=c_name(memb.name))
|
||||||
sep = '\n'
|
sep = '\n'
|
||||||
|
ret += memb.ifcond.gen_endif()
|
||||||
ret += sep
|
ret += sep
|
||||||
|
|
||||||
if base:
|
if base:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
args-if-implicit.json: In command 'test-if-cmd':
|
||||||
|
args-if-implicit.json:1: conditional command arguments require 'boxed': true
|
|
@ -0,0 +1,4 @@
|
||||||
|
{ 'command': 'test-if-cmd',
|
||||||
|
'data': {
|
||||||
|
'foo': 'int',
|
||||||
|
'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } }
|
|
@ -0,0 +1,2 @@
|
||||||
|
args-if-unboxed.json: In command 'test-if-cmd':
|
||||||
|
args-if-unboxed.json:5: conditional command arguments require 'boxed': true
|
|
@ -0,0 +1,6 @@
|
||||||
|
{ 'struct': 'TestIfCmdArgs',
|
||||||
|
'data': {
|
||||||
|
'foo': 'int',
|
||||||
|
'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } }
|
||||||
|
{ 'command': 'test-if-cmd',
|
||||||
|
'data': 'TestIfCmdArgs' }
|
|
@ -1,2 +1,2 @@
|
||||||
bad-data.json: In command 'oops':
|
bad-data.json: In command 'oops':
|
||||||
bad-data.json:2: 'data' cannot be an array
|
bad-data.json:2: 'data' should be an object or type name
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
tests/qapi-schema/event-args-if-unboxed.json: In event 'TEST_IF_EVENT':
|
||||||
|
tests/qapi-schema/event-args-if-unboxed.json:1: event's 'data' members may have 'if' conditions only with 'boxed': true
|
|
@ -0,0 +1,4 @@
|
||||||
|
{ 'event': 'TEST_IF_EVENT',
|
||||||
|
'data': {
|
||||||
|
'foo': 'int',
|
||||||
|
'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } }
|
|
@ -1,2 +1,2 @@
|
||||||
event-nest-struct.json: In event 'EVENT_A':
|
event-nest-struct.json: In event 'EVENT_A':
|
||||||
event-nest-struct.json:1: 'data' member 'a' should be a type name
|
event-nest-struct.json:1: 'data' member 'a' should be a type name or array
|
||||||
|
|
|
@ -27,6 +27,8 @@ schemas = [
|
||||||
'args-bad-boxed.json',
|
'args-bad-boxed.json',
|
||||||
'args-boxed-anon.json',
|
'args-boxed-anon.json',
|
||||||
'args-boxed-string.json',
|
'args-boxed-string.json',
|
||||||
|
'args-if-implicit.json',
|
||||||
|
'args-if-unboxed.json',
|
||||||
'args-int.json',
|
'args-int.json',
|
||||||
'args-invalid.json',
|
'args-invalid.json',
|
||||||
'args-member-array-bad.json',
|
'args-member-array-bad.json',
|
||||||
|
@ -164,6 +166,7 @@ schemas = [
|
||||||
'struct-base-clash-deep.json',
|
'struct-base-clash-deep.json',
|
||||||
'struct-base-clash.json',
|
'struct-base-clash.json',
|
||||||
'struct-data-invalid.json',
|
'struct-data-invalid.json',
|
||||||
|
'struct-data-typename.json',
|
||||||
'struct-member-if-invalid.json',
|
'struct-member-if-invalid.json',
|
||||||
'struct-member-invalid-dict.json',
|
'struct-member-invalid-dict.json',
|
||||||
'struct-member-invalid.json',
|
'struct-member-invalid.json',
|
||||||
|
@ -194,6 +197,8 @@ schemas = [
|
||||||
'union-invalid-data.json',
|
'union-invalid-data.json',
|
||||||
'union-invalid-discriminator.json',
|
'union-invalid-discriminator.json',
|
||||||
'union-invalid-if-discriminator.json',
|
'union-invalid-if-discriminator.json',
|
||||||
|
'union-invalid-union-subfield.json',
|
||||||
|
'union-invalid-union-subtype.json',
|
||||||
'union-no-base.json',
|
'union-no-base.json',
|
||||||
'union-optional-discriminator.json',
|
'union-optional-discriminator.json',
|
||||||
'union-string-discriminator.json',
|
'union-string-discriminator.json',
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
nested-struct-data.json: In command 'foo':
|
nested-struct-data.json: In command 'foo':
|
||||||
nested-struct-data.json:2: 'data' member 'a' should be a type name
|
nested-struct-data.json:2: 'data' member 'a' should be a type name or array
|
||||||
|
|
|
@ -114,6 +114,38 @@
|
||||||
{ 'struct': 'UserDefC',
|
{ 'struct': 'UserDefC',
|
||||||
'data': { 'string1': 'str', 'string2': 'str' } }
|
'data': { 'string1': 'str', 'string2': 'str' } }
|
||||||
|
|
||||||
|
# this tests that unions can contain other unions in their branches
|
||||||
|
{ 'enum': 'TestUnionEnum',
|
||||||
|
'data': [ 'value-a', 'value-b' ] }
|
||||||
|
|
||||||
|
{ 'enum': 'TestUnionEnumA',
|
||||||
|
'data': [ 'value-a1', 'value-a2' ] }
|
||||||
|
|
||||||
|
{ 'struct': 'TestUnionTypeA1',
|
||||||
|
'data': { 'integer': 'int',
|
||||||
|
'name': 'str'} }
|
||||||
|
|
||||||
|
{ 'struct': 'TestUnionTypeA2',
|
||||||
|
'data': { 'integer': 'int',
|
||||||
|
'size': 'int' } }
|
||||||
|
|
||||||
|
{ 'union': 'TestUnionTypeA',
|
||||||
|
'base': { 'type-a': 'TestUnionEnumA' },
|
||||||
|
'discriminator': 'type-a',
|
||||||
|
'data': { 'value-a1': 'TestUnionTypeA1',
|
||||||
|
'value-a2': 'TestUnionTypeA2' } }
|
||||||
|
|
||||||
|
{ 'struct': 'TestUnionTypeB',
|
||||||
|
'data': { 'integer': 'int',
|
||||||
|
'onoff': 'bool' } }
|
||||||
|
|
||||||
|
{ 'union': 'TestUnionInUnion',
|
||||||
|
'base': { 'type': 'TestUnionEnum' },
|
||||||
|
'discriminator': 'type',
|
||||||
|
'data': { 'value-a': 'TestUnionTypeA',
|
||||||
|
'value-b': 'TestUnionTypeB' } }
|
||||||
|
|
||||||
|
|
||||||
# for testing use of 'number' within alternates
|
# for testing use of 'number' within alternates
|
||||||
{ 'alternate': 'AltEnumBool', 'data': { 'e': 'EnumOne', 'b': 'bool' } }
|
{ 'alternate': 'AltEnumBool', 'data': { 'e': 'EnumOne', 'b': 'bool' } }
|
||||||
{ 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } }
|
{ 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } }
|
||||||
|
@ -220,18 +252,19 @@
|
||||||
|
|
||||||
{ 'struct': 'TestIfStruct',
|
{ 'struct': 'TestIfStruct',
|
||||||
'data': { 'foo': 'int',
|
'data': { 'foo': 'int',
|
||||||
'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_BAR'} },
|
'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_MEMBER'},
|
||||||
|
'*baz': { 'type': 'str', 'if': 'TEST_IF_STRUCT_MEMBER'} },
|
||||||
'if': 'TEST_IF_STRUCT' }
|
'if': 'TEST_IF_STRUCT' }
|
||||||
|
|
||||||
{ 'enum': 'TestIfEnum',
|
{ 'enum': 'TestIfEnum',
|
||||||
'data': [ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_BAR' } ],
|
'data': [ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_MEMBER' } ],
|
||||||
'if': 'TEST_IF_ENUM' }
|
'if': 'TEST_IF_UNION' }
|
||||||
|
|
||||||
{ 'union': 'TestIfUnion',
|
{ 'union': 'TestIfUnion',
|
||||||
'base': { 'type': 'TestIfEnum' },
|
'base': { 'type': 'TestIfEnum' },
|
||||||
'discriminator': 'type',
|
'discriminator': 'type',
|
||||||
'data': { 'foo': 'TestStruct',
|
'data': { 'foo': 'TestStruct',
|
||||||
'bar': { 'type': 'UserDefZero', 'if': 'TEST_IF_ENUM_BAR'} },
|
'bar': { 'type': 'UserDefZero', 'if': 'TEST_IF_ENUM_MEMBER'} },
|
||||||
'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'command': 'test-if-union-cmd',
|
{ 'command': 'test-if-union-cmd',
|
||||||
|
@ -240,7 +273,7 @@
|
||||||
|
|
||||||
{ 'alternate': 'TestIfAlternate',
|
{ 'alternate': 'TestIfAlternate',
|
||||||
'data': { 'foo': 'int',
|
'data': { 'foo': 'int',
|
||||||
'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_BAR'} },
|
'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_MEMBER'} },
|
||||||
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'command': 'test-if-alternate-cmd',
|
{ 'command': 'test-if-alternate-cmd',
|
||||||
|
@ -248,17 +281,16 @@
|
||||||
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'command': 'test-if-cmd',
|
{ 'command': 'test-if-cmd',
|
||||||
'data': {
|
'boxed': true,
|
||||||
'foo': 'TestIfStruct',
|
'data': 'TestIfStruct',
|
||||||
'bar': { 'type': 'TestIfEnum', 'if': 'TEST_IF_CMD_BAR' } },
|
|
||||||
'returns': 'UserDefThree',
|
'returns': 'UserDefThree',
|
||||||
'if': { 'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' }
|
{ 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' }
|
||||||
|
|
||||||
{ 'event': 'TEST_IF_EVENT',
|
{ 'event': 'TEST_IF_EVENT',
|
||||||
'data': { 'foo': 'TestIfStruct',
|
'boxed': true,
|
||||||
'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } },
|
'data': 'TestIfStruct',
|
||||||
'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'event': 'TEST_IF_EVENT2', 'data': {},
|
{ 'event': 'TEST_IF_EVENT2', 'data': {},
|
||||||
|
|
|
@ -105,6 +105,35 @@ alternate UserDefAlternate
|
||||||
object UserDefC
|
object UserDefC
|
||||||
member string1: str optional=False
|
member string1: str optional=False
|
||||||
member string2: str optional=False
|
member string2: str optional=False
|
||||||
|
enum TestUnionEnum
|
||||||
|
member value-a
|
||||||
|
member value-b
|
||||||
|
enum TestUnionEnumA
|
||||||
|
member value-a1
|
||||||
|
member value-a2
|
||||||
|
object TestUnionTypeA1
|
||||||
|
member integer: int optional=False
|
||||||
|
member name: str optional=False
|
||||||
|
object TestUnionTypeA2
|
||||||
|
member integer: int optional=False
|
||||||
|
member size: int optional=False
|
||||||
|
object q_obj_TestUnionTypeA-base
|
||||||
|
member type-a: TestUnionEnumA optional=False
|
||||||
|
object TestUnionTypeA
|
||||||
|
base q_obj_TestUnionTypeA-base
|
||||||
|
tag type-a
|
||||||
|
case value-a1: TestUnionTypeA1
|
||||||
|
case value-a2: TestUnionTypeA2
|
||||||
|
object TestUnionTypeB
|
||||||
|
member integer: int optional=False
|
||||||
|
member onoff: bool optional=False
|
||||||
|
object q_obj_TestUnionInUnion-base
|
||||||
|
member type: TestUnionEnum optional=False
|
||||||
|
object TestUnionInUnion
|
||||||
|
base q_obj_TestUnionInUnion-base
|
||||||
|
tag type
|
||||||
|
case value-a: TestUnionTypeA
|
||||||
|
case value-b: TestUnionTypeB
|
||||||
alternate AltEnumBool
|
alternate AltEnumBool
|
||||||
tag type
|
tag type
|
||||||
case e: EnumOne
|
case e: EnumOne
|
||||||
|
@ -246,13 +275,15 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> None
|
||||||
object TestIfStruct
|
object TestIfStruct
|
||||||
member foo: int optional=False
|
member foo: int optional=False
|
||||||
member bar: int optional=False
|
member bar: int optional=False
|
||||||
if TEST_IF_STRUCT_BAR
|
if TEST_IF_STRUCT_MEMBER
|
||||||
|
member baz: str optional=True
|
||||||
|
if TEST_IF_STRUCT_MEMBER
|
||||||
if TEST_IF_STRUCT
|
if TEST_IF_STRUCT
|
||||||
enum TestIfEnum
|
enum TestIfEnum
|
||||||
member foo
|
member foo
|
||||||
member bar
|
member bar
|
||||||
if TEST_IF_ENUM_BAR
|
if TEST_IF_ENUM_MEMBER
|
||||||
if TEST_IF_ENUM
|
if TEST_IF_UNION
|
||||||
object q_obj_TestIfUnion-base
|
object q_obj_TestIfUnion-base
|
||||||
member type: TestIfEnum optional=False
|
member type: TestIfEnum optional=False
|
||||||
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
|
||||||
|
@ -261,7 +292,7 @@ object TestIfUnion
|
||||||
tag type
|
tag type
|
||||||
case foo: TestStruct
|
case foo: TestStruct
|
||||||
case bar: UserDefZero
|
case bar: UserDefZero
|
||||||
if TEST_IF_ENUM_BAR
|
if TEST_IF_ENUM_MEMBER
|
||||||
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']}
|
||||||
object q_obj_test-if-union-cmd-arg
|
object q_obj_test-if-union-cmd-arg
|
||||||
member union-cmd-arg: TestIfUnion optional=False
|
member union-cmd-arg: TestIfUnion optional=False
|
||||||
|
@ -273,7 +304,7 @@ alternate TestIfAlternate
|
||||||
tag type
|
tag type
|
||||||
case foo: int
|
case foo: int
|
||||||
case bar: TestStruct
|
case bar: TestStruct
|
||||||
if TEST_IF_ALT_BAR
|
if TEST_IF_ALT_MEMBER
|
||||||
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
|
||||||
object q_obj_test-if-alternate-cmd-arg
|
object q_obj_test-if-alternate-cmd-arg
|
||||||
member alt-cmd-arg: TestIfAlternate optional=False
|
member alt-cmd-arg: TestIfAlternate optional=False
|
||||||
|
@ -281,25 +312,13 @@ object q_obj_test-if-alternate-cmd-arg
|
||||||
command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None
|
command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||||
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
|
||||||
object q_obj_test-if-cmd-arg
|
command test-if-cmd TestIfStruct -> UserDefThree
|
||||||
member foo: TestIfStruct optional=False
|
gen=True success_response=True boxed=True oob=False preconfig=False
|
||||||
member bar: TestIfEnum optional=False
|
|
||||||
if TEST_IF_CMD_BAR
|
|
||||||
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
|
|
||||||
command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree
|
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
|
||||||
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
|
||||||
command test-cmd-return-def-three None -> UserDefThree
|
command test-cmd-return-def-three None -> UserDefThree
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||||
array TestIfEnumList TestIfEnum
|
event TEST_IF_EVENT TestIfStruct
|
||||||
if TEST_IF_ENUM
|
boxed=True
|
||||||
object q_obj_TEST_IF_EVENT-arg
|
|
||||||
member foo: TestIfStruct optional=False
|
|
||||||
member bar: TestIfEnumList optional=False
|
|
||||||
if TEST_IF_EVT_BAR
|
|
||||||
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
|
|
||||||
event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg
|
|
||||||
boxed=False
|
|
||||||
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
|
||||||
event TEST_IF_EVENT2 None
|
event TEST_IF_EVENT2 None
|
||||||
boxed=False
|
boxed=False
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
returns-dict.json: In command 'oops':
|
returns-dict.json: In command 'oops':
|
||||||
returns-dict.json:2: 'returns' should be a type name
|
returns-dict.json:2: 'returns' should be a type name or array
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
struct-data-typename.json: In struct 'Stru2':
|
||||||
|
struct-data-typename.json:2: 'data' should be an object or type name
|
|
@ -0,0 +1,2 @@
|
||||||
|
{ 'struct': 'Stru1', 'data': {} }
|
||||||
|
{ 'struct': 'Stru2', 'data': 'Stru1' }
|
|
@ -1,2 +1,2 @@
|
||||||
struct-member-invalid.json: In struct 'Foo':
|
struct-member-invalid.json: In struct 'Foo':
|
||||||
struct-member-invalid.json:1: 'data' member 'a' should be a type name
|
struct-member-invalid.json:1: 'data' member 'a' should be a type name or array
|
||||||
|
|
|
@ -206,6 +206,7 @@ def main(argv):
|
||||||
parser.add_argument('-d', '--dir', action='store', default='',
|
parser.add_argument('-d', '--dir', action='store', default='',
|
||||||
help="directory containing tests")
|
help="directory containing tests")
|
||||||
parser.add_argument('-u', '--update', action='store_true',
|
parser.add_argument('-u', '--update', action='store_true',
|
||||||
|
default='QAPI_TEST_UPDATE' in os.environ,
|
||||||
help="update expected test results")
|
help="update expected test results")
|
||||||
parser.add_argument('tests', nargs='*', metavar='TEST', action='store')
|
parser.add_argument('tests', nargs='*', metavar='TEST', action='store')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
union-array-branch.json: In union 'TestUnion':
|
union-array-branch.json: In union 'TestUnion':
|
||||||
union-array-branch.json:8: 'data' member 'value1' cannot be an array
|
union-array-branch.json:8: 'data' member 'value1' should be a type name
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
union-invalid-discriminator.json: In union 'TestUnion':
|
union-invalid-discriminator.json: In union 'TestUnion':
|
||||||
union-invalid-discriminator.json:10: discriminator 'enum_wrong' is not a member of 'base'
|
union-invalid-discriminator.json:10: discriminator 'type_tag' is not a member of 'base'
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
'data': { 'integer': 'int' } }
|
'data': { 'integer': 'int' } }
|
||||||
|
|
||||||
{ 'union': 'TestUnion',
|
{ 'union': 'TestUnion',
|
||||||
'base': { 'enum1': 'TestEnum' },
|
'base': { 'type-tag': 'TestEnum' },
|
||||||
'discriminator': 'enum_wrong',
|
'discriminator': 'type_tag',
|
||||||
'data': { 'value1': 'TestTypeA',
|
'data': { 'value1': 'TestTypeA',
|
||||||
'value2': 'TestTypeB' } }
|
'value2': 'TestTypeB' } }
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
union-invalid-union-subfield.json: In union 'TestUnion':
|
||||||
|
union-invalid-union-subfield.json:25: member 'teeth' of type 'TestTypeFish' collides with base member 'teeth'
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Clash between common member and union variant's variant member
|
||||||
|
# Base's member 'teeth' clashes with TestTypeFish's
|
||||||
|
|
||||||
|
{ 'enum': 'TestEnum',
|
||||||
|
'data': [ 'animals', 'plants' ] }
|
||||||
|
|
||||||
|
{ 'enum': 'TestAnimals',
|
||||||
|
'data': [ 'fish', 'birds'] }
|
||||||
|
|
||||||
|
{ 'struct': 'TestTypeFish',
|
||||||
|
'data': { 'scales': 'int', 'teeth': 'int' } }
|
||||||
|
|
||||||
|
{ 'struct': 'TestTypeBirds',
|
||||||
|
'data': { 'feathers': 'int' } }
|
||||||
|
|
||||||
|
{ 'union': 'TestTypeAnimals',
|
||||||
|
'base': { 'atype': 'TestAnimals' },
|
||||||
|
'discriminator': 'atype',
|
||||||
|
'data': { 'fish': 'TestTypeFish',
|
||||||
|
'birds': 'TestTypeBirds' } }
|
||||||
|
|
||||||
|
{ 'struct': 'TestTypePlants',
|
||||||
|
'data': { 'integer': 'int' } }
|
||||||
|
|
||||||
|
{ 'union': 'TestUnion',
|
||||||
|
'base': { 'type': 'TestEnum',
|
||||||
|
'teeth': 'int' },
|
||||||
|
'discriminator': 'type',
|
||||||
|
'data': { 'animals': 'TestTypeAnimals',
|
||||||
|
'plants': 'TestTypePlants' } }
|
|
@ -0,0 +1,2 @@
|
||||||
|
union-invalid-union-subtype.json: In union 'TestUnion':
|
||||||
|
union-invalid-union-subtype.json:25: base member 'type' of type 'TestTypeA' collides with base member 'type'
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Clash between common member and union variant's common member
|
||||||
|
# Base's member 'type' clashes with TestTypeA's
|
||||||
|
|
||||||
|
{ 'enum': 'TestEnum',
|
||||||
|
'data': [ 'value-a', 'value-b' ] }
|
||||||
|
|
||||||
|
{ 'enum': 'TestEnumA',
|
||||||
|
'data': [ 'value-a1', 'value-a2' ] }
|
||||||
|
|
||||||
|
{ 'struct': 'TestTypeA1',
|
||||||
|
'data': { 'integer': 'int' } }
|
||||||
|
|
||||||
|
{ 'struct': 'TestTypeA2',
|
||||||
|
'data': { 'integer': 'int' } }
|
||||||
|
|
||||||
|
{ 'union': 'TestTypeA',
|
||||||
|
'base': { 'type': 'TestEnumA' },
|
||||||
|
'discriminator': 'type',
|
||||||
|
'data': { 'value-a1': 'TestTypeA1',
|
||||||
|
'value-a2': 'TestTypeA2' } }
|
||||||
|
|
||||||
|
{ 'struct': 'TestTypeB',
|
||||||
|
'data': { 'integer': 'int' } }
|
||||||
|
|
||||||
|
{ 'union': 'TestUnion',
|
||||||
|
'base': { 'type': 'TestEnum' },
|
||||||
|
'discriminator': 'type',
|
||||||
|
'data': { 'value-a': 'TestTypeA',
|
||||||
|
'value-b': 'TestTypeB' } }
|
|
@ -706,6 +706,51 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
|
||||||
g_assert(&base->enum1 == &tmp->enum1);
|
g_assert(&base->enum1 == &tmp->enum1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_union_in_union(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
Visitor *v;
|
||||||
|
g_autoptr(TestUnionInUnion) tmp = NULL;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data,
|
||||||
|
"{ 'type': 'value-a', "
|
||||||
|
" 'type-a': 'value-a1', "
|
||||||
|
" 'integer': 2, "
|
||||||
|
" 'name': 'fish' }");
|
||||||
|
|
||||||
|
visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort);
|
||||||
|
g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A);
|
||||||
|
g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A1);
|
||||||
|
g_assert_cmpint(tmp->u.value_a.u.value_a1.integer, ==, 2);
|
||||||
|
g_assert_cmpint(strcmp(tmp->u.value_a.u.value_a1.name, "fish"), ==, 0);
|
||||||
|
|
||||||
|
qapi_free_TestUnionInUnion(tmp);
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data,
|
||||||
|
"{ 'type': 'value-a', "
|
||||||
|
" 'type-a': 'value-a2', "
|
||||||
|
" 'integer': 1729, "
|
||||||
|
" 'size': 87539319 }");
|
||||||
|
|
||||||
|
visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort);
|
||||||
|
g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A);
|
||||||
|
g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A2);
|
||||||
|
g_assert_cmpint(tmp->u.value_a.u.value_a2.integer, ==, 1729);
|
||||||
|
g_assert_cmpint(tmp->u.value_a.u.value_a2.size, ==, 87539319);
|
||||||
|
|
||||||
|
qapi_free_TestUnionInUnion(tmp);
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data,
|
||||||
|
"{ 'type': 'value-b', "
|
||||||
|
" 'integer': 1729, "
|
||||||
|
" 'onoff': true }");
|
||||||
|
|
||||||
|
visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort);
|
||||||
|
g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_B);
|
||||||
|
g_assert_cmpint(tmp->u.value_b.integer, ==, 1729);
|
||||||
|
g_assert_cmpint(tmp->u.value_b.onoff, ==, true);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_visitor_in_alternate(TestInputVisitorData *data,
|
static void test_visitor_in_alternate(TestInputVisitorData *data,
|
||||||
const void *unused)
|
const void *unused)
|
||||||
{
|
{
|
||||||
|
@ -1216,6 +1261,8 @@ int main(int argc, char **argv)
|
||||||
NULL, test_visitor_in_null);
|
NULL, test_visitor_in_null);
|
||||||
input_visitor_test_add("/visitor/input/union-flat",
|
input_visitor_test_add("/visitor/input/union-flat",
|
||||||
NULL, test_visitor_in_union_flat);
|
NULL, test_visitor_in_union_flat);
|
||||||
|
input_visitor_test_add("/visitor/input/union-in-union",
|
||||||
|
NULL, test_visitor_in_union_in_union);
|
||||||
input_visitor_test_add("/visitor/input/alternate",
|
input_visitor_test_add("/visitor/input/alternate",
|
||||||
NULL, test_visitor_in_alternate);
|
NULL, test_visitor_in_alternate);
|
||||||
input_visitor_test_add("/visitor/input/errors",
|
input_visitor_test_add("/visitor/input/errors",
|
||||||
|
|
|
@ -352,6 +352,62 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
|
||||||
qapi_free_UserDefFlatUnion(tmp);
|
qapi_free_UserDefFlatUnion(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_visitor_out_union_in_union(TestOutputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
QDict *qdict;
|
||||||
|
|
||||||
|
TestUnionInUnion *tmp = g_new0(TestUnionInUnion, 1);
|
||||||
|
tmp->type = TEST_UNION_ENUM_VALUE_A;
|
||||||
|
tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A1;
|
||||||
|
tmp->u.value_a.u.value_a1.integer = 42;
|
||||||
|
tmp->u.value_a.u.value_a1.name = g_strdup("fish");
|
||||||
|
|
||||||
|
visit_type_TestUnionInUnion(data->ov, NULL, &tmp, &error_abort);
|
||||||
|
qdict = qobject_to(QDict, visitor_get(data));
|
||||||
|
g_assert(qdict);
|
||||||
|
g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "value-a");
|
||||||
|
g_assert_cmpstr(qdict_get_str(qdict, "type-a"), ==, "value-a1");
|
||||||
|
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
|
||||||
|
g_assert_cmpstr(qdict_get_str(qdict, "name"), ==, "fish");
|
||||||
|
|
||||||
|
qapi_free_TestUnionInUnion(tmp);
|
||||||
|
|
||||||
|
|
||||||
|
visitor_reset(data);
|
||||||
|
tmp = g_new0(TestUnionInUnion, 1);
|
||||||
|
tmp->type = TEST_UNION_ENUM_VALUE_A;
|
||||||
|
tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A2;
|
||||||
|
tmp->u.value_a.u.value_a2.integer = 1729;
|
||||||
|
tmp->u.value_a.u.value_a2.size = 87539319;
|
||||||
|
|
||||||
|
visit_type_TestUnionInUnion(data->ov, NULL, &tmp, &error_abort);
|
||||||
|
qdict = qobject_to(QDict, visitor_get(data));
|
||||||
|
g_assert(qdict);
|
||||||
|
g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "value-a");
|
||||||
|
g_assert_cmpstr(qdict_get_str(qdict, "type-a"), ==, "value-a2");
|
||||||
|
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1729);
|
||||||
|
g_assert_cmpint(qdict_get_int(qdict, "size"), ==, 87539319);
|
||||||
|
|
||||||
|
qapi_free_TestUnionInUnion(tmp);
|
||||||
|
|
||||||
|
|
||||||
|
visitor_reset(data);
|
||||||
|
tmp = g_new0(TestUnionInUnion, 1);
|
||||||
|
tmp->type = TEST_UNION_ENUM_VALUE_B;
|
||||||
|
tmp->u.value_b.integer = 1729;
|
||||||
|
tmp->u.value_b.onoff = true;
|
||||||
|
|
||||||
|
visit_type_TestUnionInUnion(data->ov, NULL, &tmp, &error_abort);
|
||||||
|
qdict = qobject_to(QDict, visitor_get(data));
|
||||||
|
g_assert(qdict);
|
||||||
|
g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "value-b");
|
||||||
|
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1729);
|
||||||
|
g_assert_cmpint(qdict_get_bool(qdict, "onoff"), ==, true);
|
||||||
|
|
||||||
|
qapi_free_TestUnionInUnion(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_visitor_out_alternate(TestOutputVisitorData *data,
|
static void test_visitor_out_alternate(TestOutputVisitorData *data,
|
||||||
const void *unused)
|
const void *unused)
|
||||||
{
|
{
|
||||||
|
@ -586,6 +642,8 @@ int main(int argc, char **argv)
|
||||||
&out_visitor_data, test_visitor_out_list_qapi_free);
|
&out_visitor_data, test_visitor_out_list_qapi_free);
|
||||||
output_visitor_test_add("/visitor/output/union-flat",
|
output_visitor_test_add("/visitor/output/union-flat",
|
||||||
&out_visitor_data, test_visitor_out_union_flat);
|
&out_visitor_data, test_visitor_out_union_flat);
|
||||||
|
output_visitor_test_add("/visitor/output/union-in-union",
|
||||||
|
&out_visitor_data, test_visitor_out_union_in_union);
|
||||||
output_visitor_test_add("/visitor/output/alternate",
|
output_visitor_test_add("/visitor/output/alternate",
|
||||||
&out_visitor_data, test_visitor_out_alternate);
|
&out_visitor_data, test_visitor_out_alternate);
|
||||||
output_visitor_test_add("/visitor/output/null",
|
output_visitor_test_add("/visitor/output/null",
|
||||||
|
|
Loading…
Reference in New Issue