qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3] qapi: allow unions to contain further unions
@ 2023-02-23 13:40 Daniel P. Berrangé
  2023-02-23 13:40 ` [PATCH v2 1/3] qapi: improve specificity of type/member descriptions Daniel P. Berrangé
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2023-02-23 13:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: Eric Blake, Michael Roth, Markus Armbruster, Het Gala,
	Daniel P. Berrangé

Currently it is not possible for a union type to contain a
further union as one (or more) of its branches. This relaxes
that restriction and adds the calls needed to validate field
name uniqueness as unions are flattened.

In v2:

  * Improve specificity of type/members descriptions for
    error reporting scenarios
  * Make it easier to regenerate qapi test output
  * Move expected "good data" into qapi-schema-test.json
  * Add description to "bad data" test files
  * Add unit tests to cover union-in-union serialization
    / deserialization to/from JSON

Daniel P. Berrangé (3):
  qapi: improve specificity of type/member descriptions
  qapi: use env var to trigger qapi test output updates
  qapi: allow unions to contain further unions

 scripts/qapi/schema.py                        | 11 ++--
 tests/qapi-schema/alternate-any.err           |  2 +-
 .../alternate-conflict-bool-string.err        |  2 +-
 tests/qapi-schema/alternate-conflict-dict.err |  2 +-
 .../alternate-conflict-enum-bool.err          |  2 +-
 .../alternate-conflict-enum-int.err           |  2 +-
 .../qapi-schema/alternate-conflict-lists.err  |  2 +-
 .../alternate-conflict-num-string.err         |  2 +-
 .../qapi-schema/alternate-conflict-string.err |  2 +-
 tests/qapi-schema/alternate-nested.err        |  2 +-
 tests/qapi-schema/alternate-unknown.err       |  2 +-
 tests/qapi-schema/args-member-unknown.err     |  2 +-
 tests/qapi-schema/enum-clash-member.err       |  2 +-
 tests/qapi-schema/features-duplicate-name.err |  2 +-
 tests/qapi-schema/meson.build                 |  2 +
 tests/qapi-schema/qapi-schema-test.json       | 32 ++++++++++
 tests/qapi-schema/qapi-schema-test.out        | 29 ++++++++++
 tests/qapi-schema/struct-base-clash-deep.err  |  2 +-
 tests/qapi-schema/struct-base-clash.err       |  2 +-
 .../qapi-schema/struct-member-name-clash.err  |  2 +-
 tests/qapi-schema/test-qapi.py                |  3 +-
 tests/qapi-schema/union-bad-base.err          |  2 +-
 tests/qapi-schema/union-int-branch.err        |  2 +-
 .../union-invalid-union-subfield.err          |  2 +
 .../union-invalid-union-subfield.json         | 30 ++++++++++
 .../union-invalid-union-subfield.out          |  0
 .../union-invalid-union-subtype.err           |  2 +
 .../union-invalid-union-subtype.json          | 29 ++++++++++
 .../union-invalid-union-subtype.out           |  0
 tests/qapi-schema/union-unknown.err           |  2 +-
 tests/unit/test-qobject-input-visitor.c       | 47 +++++++++++++++
 tests/unit/test-qobject-output-visitor.c      | 58 +++++++++++++++++++
 32 files changed, 257 insertions(+), 26 deletions(-)
 create mode 100644 tests/qapi-schema/union-invalid-union-subfield.err
 create mode 100644 tests/qapi-schema/union-invalid-union-subfield.json
 create mode 100644 tests/qapi-schema/union-invalid-union-subfield.out
 create mode 100644 tests/qapi-schema/union-invalid-union-subtype.err
 create mode 100644 tests/qapi-schema/union-invalid-union-subtype.json
 create mode 100644 tests/qapi-schema/union-invalid-union-subtype.out

-- 
2.39.2



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/3] qapi: improve specificity of type/member descriptions
  2023-02-23 13:40 [PATCH v2 0/3] qapi: allow unions to contain further unions Daniel P. Berrangé
@ 2023-02-23 13:40 ` Daniel P. Berrangé
  2023-03-17 12:08   ` Markus Armbruster
  2023-02-23 13:40 ` [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates Daniel P. Berrangé
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Daniel P. Berrangé @ 2023-02-23 13:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: Eric Blake, Michael Roth, Markus Armbruster, Het Gala,
	Daniel P. Berrangé

When describing member types always include the context of the
containing type. Although this is often redundant, in some cases
it will help to reduce ambiguity.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 scripts/qapi/schema.py                               | 5 ++---
 tests/qapi-schema/alternate-any.err                  | 2 +-
 tests/qapi-schema/alternate-conflict-bool-string.err | 2 +-
 tests/qapi-schema/alternate-conflict-dict.err        | 2 +-
 tests/qapi-schema/alternate-conflict-enum-bool.err   | 2 +-
 tests/qapi-schema/alternate-conflict-enum-int.err    | 2 +-
 tests/qapi-schema/alternate-conflict-lists.err       | 2 +-
 tests/qapi-schema/alternate-conflict-num-string.err  | 2 +-
 tests/qapi-schema/alternate-conflict-string.err      | 2 +-
 tests/qapi-schema/alternate-nested.err               | 2 +-
 tests/qapi-schema/alternate-unknown.err              | 2 +-
 tests/qapi-schema/args-member-unknown.err            | 2 +-
 tests/qapi-schema/enum-clash-member.err              | 2 +-
 tests/qapi-schema/features-duplicate-name.err        | 2 +-
 tests/qapi-schema/struct-base-clash-deep.err         | 2 +-
 tests/qapi-schema/struct-base-clash.err              | 2 +-
 tests/qapi-schema/struct-member-name-clash.err       | 2 +-
 tests/qapi-schema/union-bad-base.err                 | 2 +-
 tests/qapi-schema/union-int-branch.err               | 2 +-
 tests/qapi-schema/union-unknown.err                  | 2 +-
 20 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index cd8661125c..6c481ab0c0 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -713,9 +713,8 @@ def describe(self, info):
                 role = 'base ' + role
             else:
                 assert False
-        elif defined_in != info.defn_name:
-            return "%s '%s' of type '%s'" % (role, self.name, defined_in)
-        return "%s '%s'" % (role, self.name)
+
+        return "%s '%s' of type '%s'" % (role, self.name, defined_in)
 
 
 class QAPISchemaEnumMember(QAPISchemaMember):
diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err
index baeb3f66d1..6c12358a88 100644
--- a/tests/qapi-schema/alternate-any.err
+++ b/tests/qapi-schema/alternate-any.err
@@ -1,2 +1,2 @@
 alternate-any.json: In alternate 'Alt':
-alternate-any.json:2: branch 'one' cannot use built-in type 'any'
+alternate-any.json:2: branch 'one' of type 'Alt' cannot use built-in type 'any'
diff --git a/tests/qapi-schema/alternate-conflict-bool-string.err b/tests/qapi-schema/alternate-conflict-bool-string.err
index 59ff5efa87..d7fd625632 100644
--- a/tests/qapi-schema/alternate-conflict-bool-string.err
+++ b/tests/qapi-schema/alternate-conflict-bool-string.err
@@ -1,2 +1,2 @@
 alternate-conflict-bool-string.json: In alternate 'Alt':
-alternate-conflict-bool-string.json:2: branch 'two' can't be distinguished from 'one'
+alternate-conflict-bool-string.json:2: branch 'two' of type 'Alt' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
index d4970284ba..05174ab4f1 100644
--- a/tests/qapi-schema/alternate-conflict-dict.err
+++ b/tests/qapi-schema/alternate-conflict-dict.err
@@ -1,2 +1,2 @@
 alternate-conflict-dict.json: In alternate 'Alt':
-alternate-conflict-dict.json:6: branch 'two' can't be distinguished from 'one'
+alternate-conflict-dict.json:6: branch 'two' of type 'Alt' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.err b/tests/qapi-schema/alternate-conflict-enum-bool.err
index 5f35855274..02ed66b0bf 100644
--- a/tests/qapi-schema/alternate-conflict-enum-bool.err
+++ b/tests/qapi-schema/alternate-conflict-enum-bool.err
@@ -1,2 +1,2 @@
 alternate-conflict-enum-bool.json: In alternate 'Alt':
-alternate-conflict-enum-bool.json:4: branch 'two' can't be distinguished from 'one'
+alternate-conflict-enum-bool.json:4: branch 'two' of type 'Alt' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-enum-int.err b/tests/qapi-schema/alternate-conflict-enum-int.err
index 6a6d156664..1874558f49 100644
--- a/tests/qapi-schema/alternate-conflict-enum-int.err
+++ b/tests/qapi-schema/alternate-conflict-enum-int.err
@@ -1,2 +1,2 @@
 alternate-conflict-enum-int.json: In alternate 'Alt':
-alternate-conflict-enum-int.json:4: branch 'two' can't be distinguished from 'one'
+alternate-conflict-enum-int.json:4: branch 'two' of type 'Alt' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-lists.err b/tests/qapi-schema/alternate-conflict-lists.err
index f3374ec1e7..de480dc86b 100644
--- a/tests/qapi-schema/alternate-conflict-lists.err
+++ b/tests/qapi-schema/alternate-conflict-lists.err
@@ -1,2 +1,2 @@
 alternate-conflict-lists.json: In alternate 'Alt':
-alternate-conflict-lists.json:4: branch 'two' can't be distinguished from 'one'
+alternate-conflict-lists.json:4: branch 'two' of type 'Alt' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-num-string.err b/tests/qapi-schema/alternate-conflict-num-string.err
index 38c805ea1f..0d21ec590c 100644
--- a/tests/qapi-schema/alternate-conflict-num-string.err
+++ b/tests/qapi-schema/alternate-conflict-num-string.err
@@ -1,2 +1,2 @@
 alternate-conflict-num-string.json: In alternate 'Alt':
-alternate-conflict-num-string.json:2: branch 'two' can't be distinguished from 'one'
+alternate-conflict-num-string.json:2: branch 'two' of type 'Alt' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
index 2fa08193db..97097cae97 100644
--- a/tests/qapi-schema/alternate-conflict-string.err
+++ b/tests/qapi-schema/alternate-conflict-string.err
@@ -1,2 +1,2 @@
 alternate-conflict-string.json: In alternate 'Alt':
-alternate-conflict-string.json:2: branch 'two' can't be distinguished from 'one'
+alternate-conflict-string.json:2: branch 'two' of type 'Alt' can't be distinguished from 'one'
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
index 3ae9cd2f11..5603aa95e3 100644
--- a/tests/qapi-schema/alternate-nested.err
+++ b/tests/qapi-schema/alternate-nested.err
@@ -1,2 +1,2 @@
 alternate-nested.json: In alternate 'Alt2':
-alternate-nested.json:4: branch 'nested' cannot use alternate type 'Alt1'
+alternate-nested.json:4: branch 'nested' of type 'Alt2' cannot use alternate type 'Alt1'
diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
index 17fec1cd17..befd207a76 100644
--- a/tests/qapi-schema/alternate-unknown.err
+++ b/tests/qapi-schema/alternate-unknown.err
@@ -1,2 +1,2 @@
 alternate-unknown.json: In alternate 'Alt':
-alternate-unknown.json:2: branch 'unknown' uses unknown type 'MissingType'
+alternate-unknown.json:2: branch 'unknown' of type 'Alt' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/args-member-unknown.err b/tests/qapi-schema/args-member-unknown.err
index 96b6e5d289..b24c2ae572 100644
--- a/tests/qapi-schema/args-member-unknown.err
+++ b/tests/qapi-schema/args-member-unknown.err
@@ -1,2 +1,2 @@
 args-member-unknown.json: In command 'oops':
-args-member-unknown.json:2: parameter 'member' uses unknown type 'NoSuchType'
+args-member-unknown.json:2: parameter 'member' of type 'oops-arg' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
index e4eb102ae2..0ede36d68e 100644
--- a/tests/qapi-schema/enum-clash-member.err
+++ b/tests/qapi-schema/enum-clash-member.err
@@ -1,2 +1,2 @@
 enum-clash-member.json: In enum 'MyEnum':
-enum-clash-member.json:3: value 'one_two' collides with value 'one-two'
+enum-clash-member.json:3: value 'one_two' of type 'MyEnum' collides with value 'one-two' of type 'MyEnum'
diff --git a/tests/qapi-schema/features-duplicate-name.err b/tests/qapi-schema/features-duplicate-name.err
index 0adbee6b0a..ffd124f5b0 100644
--- a/tests/qapi-schema/features-duplicate-name.err
+++ b/tests/qapi-schema/features-duplicate-name.err
@@ -1,2 +1,2 @@
 features-duplicate-name.json: In struct 'FeatureStruct0':
-features-duplicate-name.json:1: feature 'foo' collides with feature 'foo'
+features-duplicate-name.json:1: feature 'foo' of type 'FeatureStruct0' collides with feature 'foo' of type 'FeatureStruct0'
diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err
index 79879681d9..632f78b6c0 100644
--- a/tests/qapi-schema/struct-base-clash-deep.err
+++ b/tests/qapi-schema/struct-base-clash-deep.err
@@ -1,2 +1,2 @@
 struct-base-clash-deep.json: In struct 'Sub':
-struct-base-clash-deep.json:10: member 'name' collides with member 'name' of type 'Base'
+struct-base-clash-deep.json:10: member 'name' of type 'Sub' collides with member 'name' of type 'Base'
diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err
index 46473947e6..1d54079c16 100644
--- a/tests/qapi-schema/struct-base-clash.err
+++ b/tests/qapi-schema/struct-base-clash.err
@@ -1,2 +1,2 @@
 struct-base-clash.json: In struct 'Sub':
-struct-base-clash.json:5: member 'name' collides with member 'name' of type 'Base'
+struct-base-clash.json:5: member 'name' of type 'Sub' collides with member 'name' of type 'Base'
diff --git a/tests/qapi-schema/struct-member-name-clash.err b/tests/qapi-schema/struct-member-name-clash.err
index 7e53a605d2..ebf66cd5b8 100644
--- a/tests/qapi-schema/struct-member-name-clash.err
+++ b/tests/qapi-schema/struct-member-name-clash.err
@@ -1,2 +1,2 @@
 struct-member-name-clash.json: In struct 'Oops':
-struct-member-name-clash.json:5: member 'a_b' collides with member 'a-b'
+struct-member-name-clash.json:5: member 'a_b' of type 'Oops' collides with member 'a-b' of type 'Oops'
diff --git a/tests/qapi-schema/union-bad-base.err b/tests/qapi-schema/union-bad-base.err
index 42b2ed1dda..9f92b35a07 100644
--- a/tests/qapi-schema/union-bad-base.err
+++ b/tests/qapi-schema/union-bad-base.err
@@ -1,2 +1,2 @@
 union-bad-base.json: In union 'TestUnion':
-union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string'
+union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string' of type 'TestUnion-base'
diff --git a/tests/qapi-schema/union-int-branch.err b/tests/qapi-schema/union-int-branch.err
index 8fdc81edd1..302e3976e0 100644
--- a/tests/qapi-schema/union-int-branch.err
+++ b/tests/qapi-schema/union-int-branch.err
@@ -1,2 +1,2 @@
 union-int-branch.json: In union 'TestUnion':
-union-int-branch.json:8: branch 'value1' cannot use built-in type 'int'
+union-int-branch.json:8: branch 'value1' of type 'TestUnion' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
index dad79beae0..e60ab9421a 100644
--- a/tests/qapi-schema/union-unknown.err
+++ b/tests/qapi-schema/union-unknown.err
@@ -1,2 +1,2 @@
 union-unknown.json: In union 'Union':
-union-unknown.json:3: branch 'unknown' uses unknown type 'MissingType'
+union-unknown.json:3: branch 'unknown' of type 'Union' uses unknown type 'MissingType'
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates
  2023-02-23 13:40 [PATCH v2 0/3] qapi: allow unions to contain further unions Daniel P. Berrangé
  2023-02-23 13:40 ` [PATCH v2 1/3] qapi: improve specificity of type/member descriptions Daniel P. Berrangé
@ 2023-02-23 13:40 ` Daniel P. Berrangé
  2023-02-24 19:28   ` Eric Blake
  2023-03-17 12:05   ` Markus Armbruster
  2023-02-23 13:40 ` [PATCH v2 3/3] qapi: allow unions to contain further unions Daniel P. Berrangé
  2023-03-17 15:55 ` [PATCH v2 0/3] " Markus Armbruster
  3 siblings, 2 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2023-02-23 13:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: Eric Blake, Michael Roth, Markus Armbruster, Het Gala,
	Daniel P. Berrangé

It is possible to pass --update to tests/qapi-schema/test-qapi.py
to make it update the output files on error. This is inconvient
to achieve though when test-qapi.py is run indirectly by make/meson.

Instead simply allow for an env variable to be set:

 $ QAPI_TEST_UPDATE=1 make check-qapi-schema

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 tests/qapi-schema/test-qapi.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 2160cef082..75f2759fd6 100755
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -215,7 +215,8 @@ def main(argv):
         (dir_name, base_name) = os.path.split(t)
         dir_name = dir_name or args.dir
         test_name = os.path.splitext(base_name)[0]
-        status |= test_and_diff(test_name, dir_name, args.update)
+        update = args.update or "QAPI_TEST_UPDATE" in os.environ
+        status |= test_and_diff(test_name, dir_name, update)
 
     exit(status)
 
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 3/3] qapi: allow unions to contain further unions
  2023-02-23 13:40 [PATCH v2 0/3] qapi: allow unions to contain further unions Daniel P. Berrangé
  2023-02-23 13:40 ` [PATCH v2 1/3] qapi: improve specificity of type/member descriptions Daniel P. Berrangé
  2023-02-23 13:40 ` [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates Daniel P. Berrangé
@ 2023-02-23 13:40 ` Daniel P. Berrangé
  2023-03-07  3:53   ` Het Gala
  2023-03-17 15:48   ` Markus Armbruster
  2023-03-17 15:55 ` [PATCH v2 0/3] " Markus Armbruster
  3 siblings, 2 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2023-02-23 13:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: Eric Blake, Michael Roth, Markus Armbruster, Het Gala,
	Daniel P. Berrangé

This extends the QAPI schema validation to permit unions inside unions,
provided the checks for clashing fields pass.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
 scripts/qapi/schema.py                        |  6 +-
 tests/qapi-schema/meson.build                 |  2 +
 tests/qapi-schema/qapi-schema-test.json       | 32 ++++++++++
 tests/qapi-schema/qapi-schema-test.out        | 29 ++++++++++
 .../union-invalid-union-subfield.err          |  2 +
 .../union-invalid-union-subfield.json         | 30 ++++++++++
 .../union-invalid-union-subfield.out          |  0
 .../union-invalid-union-subtype.err           |  2 +
 .../union-invalid-union-subtype.json          | 29 ++++++++++
 .../union-invalid-union-subtype.out           |  0
 tests/unit/test-qobject-input-visitor.c       | 47 +++++++++++++++
 tests/unit/test-qobject-output-visitor.c      | 58 +++++++++++++++++++
 12 files changed, 234 insertions(+), 3 deletions(-)
 create mode 100644 tests/qapi-schema/union-invalid-union-subfield.err
 create mode 100644 tests/qapi-schema/union-invalid-union-subfield.json
 create mode 100644 tests/qapi-schema/union-invalid-union-subfield.out
 create mode 100644 tests/qapi-schema/union-invalid-union-subtype.err
 create mode 100644 tests/qapi-schema/union-invalid-union-subtype.json
 create mode 100644 tests/qapi-schema/union-invalid-union-subtype.out

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 6c481ab0c0..5c4457f789 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -465,9 +465,10 @@ def check(self, schema):
     # on behalf of info, which is not necessarily self.info
     def check_clash(self, info, seen):
         assert self._checked
-        assert not self.variants       # not implemented
         for m in self.members:
             m.check_clash(info, seen)
+        if self.variants:
+            self.variants.check_clash(info, seen)
 
     def connect_doc(self, doc=None):
         super().connect_doc(doc)
@@ -652,8 +653,7 @@ def check(self, schema, seen):
                         self.info,
                         "branch '%s' is not a value of %s"
                         % (v.name, self.tag_member.type.describe()))
-                if (not isinstance(v.type, QAPISchemaObjectType)
-                        or v.type.variants):
+                if not isinstance(v.type, QAPISchemaObjectType):
                     raise QAPISemError(
                         self.info,
                         "%s cannot use %s"
diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build
index d85b14f28c..1591eb322b 100644
--- a/tests/qapi-schema/meson.build
+++ b/tests/qapi-schema/meson.build
@@ -194,6 +194,8 @@ schemas = [
   'union-invalid-data.json',
   'union-invalid-discriminator.json',
   'union-invalid-if-discriminator.json',
+  'union-invalid-union-subfield.json',
+  'union-invalid-union-subtype.json',
   'union-no-base.json',
   'union-optional-discriminator.json',
   'union-string-discriminator.json',
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index ba7302f42b..40f1a3d88d 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -114,6 +114,38 @@
 { 'struct': 'UserDefC',
   '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
 { 'alternate': 'AltEnumBool', 'data': { 'e': 'EnumOne', 'b': 'bool' } }
 { 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 043d75c655..9fe1038944 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -105,6 +105,35 @@ alternate UserDefAlternate
 object UserDefC
     member string1: 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
     tag type
     case e: EnumOne
diff --git a/tests/qapi-schema/union-invalid-union-subfield.err b/tests/qapi-schema/union-invalid-union-subfield.err
new file mode 100644
index 0000000000..43574dea79
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subfield.err
@@ -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' of type 'TestUnion-base'
diff --git a/tests/qapi-schema/union-invalid-union-subfield.json b/tests/qapi-schema/union-invalid-union-subfield.json
new file mode 100644
index 0000000000..e1639d3a96
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subfield.json
@@ -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' } }
diff --git a/tests/qapi-schema/union-invalid-union-subfield.out b/tests/qapi-schema/union-invalid-union-subfield.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/union-invalid-union-subtype.err b/tests/qapi-schema/union-invalid-union-subtype.err
new file mode 100644
index 0000000000..e45f330cec
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subtype.err
@@ -0,0 +1,2 @@
+union-invalid-union-subtype.json: In union 'TestUnion':
+union-invalid-union-subtype.json:25: base member 'type' of type 'TestTypeA-base' collides with base member 'type' of type 'TestUnion-base'
diff --git a/tests/qapi-schema/union-invalid-union-subtype.json b/tests/qapi-schema/union-invalid-union-subtype.json
new file mode 100644
index 0000000000..ce1de51d8d
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-union-subtype.json
@@ -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' } }
diff --git a/tests/qapi-schema/union-invalid-union-subtype.out b/tests/qapi-schema/union-invalid-union-subtype.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c
index 77fbf985be..9b3e2dbe14 100644
--- a/tests/unit/test-qobject-input-visitor.c
+++ b/tests/unit/test-qobject-input-visitor.c
@@ -706,6 +706,51 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
     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,
                                       const void *unused)
 {
@@ -1216,6 +1261,8 @@ int main(int argc, char **argv)
                            NULL, test_visitor_in_null);
     input_visitor_test_add("/visitor/input/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",
                            NULL, test_visitor_in_alternate);
     input_visitor_test_add("/visitor/input/errors",
diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c
index 7f054289fe..1535b3ad17 100644
--- a/tests/unit/test-qobject-output-visitor.c
+++ b/tests/unit/test-qobject-output-visitor.c
@@ -352,6 +352,62 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
     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,
                                        const void *unused)
 {
@@ -586,6 +642,8 @@ int main(int argc, char **argv)
                             &out_visitor_data, test_visitor_out_list_qapi_free);
     output_visitor_test_add("/visitor/output/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",
                             &out_visitor_data, test_visitor_out_alternate);
     output_visitor_test_add("/visitor/output/null",
-- 
2.39.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates
  2023-02-23 13:40 ` [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates Daniel P. Berrangé
@ 2023-02-24 19:28   ` Eric Blake
  2023-03-17 12:05   ` Markus Armbruster
  1 sibling, 0 replies; 13+ messages in thread
From: Eric Blake @ 2023-02-24 19:28 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Michael Roth, Markus Armbruster, Het Gala

On Thu, Feb 23, 2023 at 01:40:26PM +0000, Daniel P. Berrangé wrote:
> It is possible to pass --update to tests/qapi-schema/test-qapi.py
> to make it update the output files on error. This is inconvient

inconvenient

> to achieve though when test-qapi.py is run indirectly by make/meson.
> 
> Instead simply allow for an env variable to be set:
> 
>  $ QAPI_TEST_UPDATE=1 make check-qapi-schema
> 
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  tests/qapi-schema/test-qapi.py | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index 2160cef082..75f2759fd6 100755
> --- a/tests/qapi-schema/test-qapi.py
> +++ b/tests/qapi-schema/test-qapi.py
> @@ -215,7 +215,8 @@ def main(argv):
>          (dir_name, base_name) = os.path.split(t)
>          dir_name = dir_name or args.dir
>          test_name = os.path.splitext(base_name)[0]
> -        status |= test_and_diff(test_name, dir_name, args.update)
> +        update = args.update or "QAPI_TEST_UPDATE" in os.environ
> +        status |= test_and_diff(test_name, dir_name, update)

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 3/3] qapi: allow unions to contain further unions
  2023-02-23 13:40 ` [PATCH v2 3/3] qapi: allow unions to contain further unions Daniel P. Berrangé
@ 2023-03-07  3:53   ` Het Gala
  2023-03-17 15:48   ` Markus Armbruster
  1 sibling, 0 replies; 13+ messages in thread
From: Het Gala @ 2023-03-07  3:53 UTC (permalink / raw)
  To: Daniel P. Berrangé, qemu-devel
  Cc: Eric Blake, Michael Roth, Markus Armbruster

Hi,


On 23/02/23 7:10 pm, Daniel P. Berrangé wrote:
> This extends the QAPI schema validation to permit unions inside unions,
> provided the checks for clashing fields pass.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>   scripts/qapi/schema.py                        |  6 +-
>   tests/qapi-schema/meson.build                 |  2 +
>   tests/qapi-schema/qapi-schema-test.json       | 32 ++++++++++
>   tests/qapi-schema/qapi-schema-test.out        | 29 ++++++++++
>   .../union-invalid-union-subfield.err          |  2 +
>   .../union-invalid-union-subfield.json         | 30 ++++++++++
>   .../union-invalid-union-subfield.out          |  0
>   .../union-invalid-union-subtype.err           |  2 +
>   .../union-invalid-union-subtype.json          | 29 ++++++++++
>   .../union-invalid-union-subtype.out           |  0
>   tests/unit/test-qobject-input-visitor.c       | 47 +++++++++++++++
>   tests/unit/test-qobject-output-visitor.c      | 58 +++++++++++++++++++
>   12 files changed, 234 insertions(+), 3 deletions(-)
>   create mode 100644 tests/qapi-schema/union-invalid-union-subfield.err
>   create mode 100644 tests/qapi-schema/union-invalid-union-subfield.json
>   create mode 100644 tests/qapi-schema/union-invalid-union-subfield.out
>   create mode 100644 tests/qapi-schema/union-invalid-union-subtype.err
>   create mode 100644 tests/qapi-schema/union-invalid-union-subtype.json
>   create mode 100644 tests/qapi-schema/union-invalid-union-subtype.out
>
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index 6c481ab0c0..5c4457f789 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -465,9 +465,10 @@ def check(self, schema):
>       # on behalf of info, which is not necessarily self.info
>       def check_clash(self, info, seen):
>           assert self._checked
> -        assert not self.variants       # not implemented
>           for m in self.members:
>               m.check_clash(info, seen)
> +        if self.variants:
> +            self.variants.check_clash(info, seen)
>   
>       def connect_doc(self, doc=None):
>           super().connect_doc(doc)
> @@ -652,8 +653,7 @@ def check(self, schema, seen):
>                           self.info,
>                           "branch '%s' is not a value of %s"
>                           % (v.name, self.tag_member.type.describe()))
> -                if (not isinstance(v.type, QAPISchemaObjectType)
> -                        or v.type.variants):
> +                if not isinstance(v.type, QAPISchemaObjectType):
>                       raise QAPISemError(
>                           self.info,
>                           "%s cannot use %s"
> diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build
> index d85b14f28c..1591eb322b 100644
> --- a/tests/qapi-schema/meson.build
> +++ b/tests/qapi-schema/meson.build
> @@ -194,6 +194,8 @@ schemas = [
>     'union-invalid-data.json',
>     'union-invalid-discriminator.json',
>     'union-invalid-if-discriminator.json',
> +  'union-invalid-union-subfield.json',
> +  'union-invalid-union-subtype.json',
>     'union-no-base.json',
>     'union-optional-discriminator.json',
>     'union-string-discriminator.json',
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index ba7302f42b..40f1a3d88d 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -114,6 +114,38 @@
>   { 'struct': 'UserDefC',
>     '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
>   { 'alternate': 'AltEnumBool', 'data': { 'e': 'EnumOne', 'b': 'bool' } }
>   { 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } }
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 043d75c655..9fe1038944 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -105,6 +105,35 @@ alternate UserDefAlternate
>   object UserDefC
>       member string1: 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
>       tag type
>       case e: EnumOne
> diff --git a/tests/qapi-schema/union-invalid-union-subfield.err b/tests/qapi-schema/union-invalid-union-subfield.err
> new file mode 100644
> index 0000000000..43574dea79
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subfield.err
> @@ -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' of type 'TestUnion-base'
> diff --git a/tests/qapi-schema/union-invalid-union-subfield.json b/tests/qapi-schema/union-invalid-union-subfield.json
> new file mode 100644
> index 0000000000..e1639d3a96
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subfield.json
> @@ -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' } }
> diff --git a/tests/qapi-schema/union-invalid-union-subfield.out b/tests/qapi-schema/union-invalid-union-subfield.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/union-invalid-union-subtype.err b/tests/qapi-schema/union-invalid-union-subtype.err
> new file mode 100644
> index 0000000000..e45f330cec
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subtype.err
> @@ -0,0 +1,2 @@
> +union-invalid-union-subtype.json: In union 'TestUnion':
> +union-invalid-union-subtype.json:25: base member 'type' of type 'TestTypeA-base' collides with base member 'type' of type 'TestUnion-base'
> diff --git a/tests/qapi-schema/union-invalid-union-subtype.json b/tests/qapi-schema/union-invalid-union-subtype.json
> new file mode 100644
> index 0000000000..ce1de51d8d
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subtype.json
> @@ -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' } }
> diff --git a/tests/qapi-schema/union-invalid-union-subtype.out b/tests/qapi-schema/union-invalid-union-subtype.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c
> index 77fbf985be..9b3e2dbe14 100644
> --- a/tests/unit/test-qobject-input-visitor.c
> +++ b/tests/unit/test-qobject-input-visitor.c
> @@ -706,6 +706,51 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
>       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,
>                                         const void *unused)
>   {
> @@ -1216,6 +1261,8 @@ int main(int argc, char **argv)
>                              NULL, test_visitor_in_null);
>       input_visitor_test_add("/visitor/input/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",
>                              NULL, test_visitor_in_alternate);
>       input_visitor_test_add("/visitor/input/errors",
> diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c
> index 7f054289fe..1535b3ad17 100644
> --- a/tests/unit/test-qobject-output-visitor.c
> +++ b/tests/unit/test-qobject-output-visitor.c
> @@ -352,6 +352,62 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
>       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,
>                                          const void *unused)
>   {
> @@ -586,6 +642,8 @@ int main(int argc, char **argv)
>                               &out_visitor_data, test_visitor_out_list_qapi_free);
>       output_visitor_test_add("/visitor/output/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",
>                               &out_visitor_data, test_visitor_out_alternate);
>       output_visitor_test_add("/visitor/output/null",

The changes look good to me. I have tried to built migrate QAPI changes 
on top of Daniel's v1 changes, and it was successful.

Once Markus and other maintainers approves this patchset, I can post v4 
version of 'migrate' qapi changes on top of this change.

Looking forward for replies on this patchset :)

Regards,
Het Gala


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates
  2023-02-23 13:40 ` [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates Daniel P. Berrangé
  2023-02-24 19:28   ` Eric Blake
@ 2023-03-17 12:05   ` Markus Armbruster
  1 sibling, 0 replies; 13+ messages in thread
From: Markus Armbruster @ 2023-03-17 12:05 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, Eric Blake, Michael Roth, Het Gala

I have a mild dislike of abbreviations like "env var".  Perhaps:

    qapi: Support updating expected test output via make

Daniel P. Berrangé <berrange@redhat.com> writes:

> It is possible to pass --update to tests/qapi-schema/test-qapi.py
> to make it update the output files on error. This is inconvient
> to achieve though when test-qapi.py is run indirectly by make/meson.
>
> Instead simply allow for an env variable to be set:
>
>  $ QAPI_TEST_UPDATE=1 make check-qapi-schema

Suggest to omit the value

    $ QAPI_TEST_UPDATE= make check-qapi-schema

The value doesn't actually matter.  A value of 1 looks as if it did, and
as if a value of 0 would disable the thing.

I'm no fan of environment variables duplicating options, but I
understand the desire for a make invocation, and I don't have a better
idea.

> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  tests/qapi-schema/test-qapi.py | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index 2160cef082..75f2759fd6 100755
> --- a/tests/qapi-schema/test-qapi.py
> +++ b/tests/qapi-schema/test-qapi.py
> @@ -215,7 +215,8 @@ def main(argv):
>          (dir_name, base_name) = os.path.split(t)
>          dir_name = dir_name or args.dir
>          test_name = os.path.splitext(base_name)[0]
> -        status |= test_and_diff(test_name, dir_name, args.update)
> +        update = args.update or "QAPI_TEST_UPDATE" in os.environ
> +        status |= test_and_diff(test_name, dir_name, update)
>  
>      exit(status)

Let's use argparse instead:

       parser.add_argument('-u', '--update', action='store_true',
  +                        default='QAPI_TEST_UPDATE' in os.environ,
                           help="update expected test results")



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 1/3] qapi: improve specificity of type/member descriptions
  2023-02-23 13:40 ` [PATCH v2 1/3] qapi: improve specificity of type/member descriptions Daniel P. Berrangé
@ 2023-03-17 12:08   ` Markus Armbruster
  0 siblings, 0 replies; 13+ messages in thread
From: Markus Armbruster @ 2023-03-17 12:08 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, Eric Blake, Michael Roth, Het Gala

Daniel P. Berrangé <berrange@redhat.com> writes:

> When describing member types always include the context of the
> containing type. Although this is often redundant, in some cases
> it will help to reduce ambiguity.

Unfortunately, it can also be confusing, as we shall see below.

> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  scripts/qapi/schema.py                               | 5 ++---
>  tests/qapi-schema/alternate-any.err                  | 2 +-
>  tests/qapi-schema/alternate-conflict-bool-string.err | 2 +-
>  tests/qapi-schema/alternate-conflict-dict.err        | 2 +-
>  tests/qapi-schema/alternate-conflict-enum-bool.err   | 2 +-
>  tests/qapi-schema/alternate-conflict-enum-int.err    | 2 +-
>  tests/qapi-schema/alternate-conflict-lists.err       | 2 +-
>  tests/qapi-schema/alternate-conflict-num-string.err  | 2 +-
>  tests/qapi-schema/alternate-conflict-string.err      | 2 +-
>  tests/qapi-schema/alternate-nested.err               | 2 +-
>  tests/qapi-schema/alternate-unknown.err              | 2 +-
>  tests/qapi-schema/args-member-unknown.err            | 2 +-
>  tests/qapi-schema/enum-clash-member.err              | 2 +-
>  tests/qapi-schema/features-duplicate-name.err        | 2 +-
>  tests/qapi-schema/struct-base-clash-deep.err         | 2 +-
>  tests/qapi-schema/struct-base-clash.err              | 2 +-
>  tests/qapi-schema/struct-member-name-clash.err       | 2 +-
>  tests/qapi-schema/union-bad-base.err                 | 2 +-
>  tests/qapi-schema/union-int-branch.err               | 2 +-
>  tests/qapi-schema/union-unknown.err                  | 2 +-
>  20 files changed, 21 insertions(+), 22 deletions(-)
>
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index cd8661125c..6c481ab0c0 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -713,9 +713,8 @@ def describe(self, info):
>                  role = 'base ' + role
>              else:
>                  assert False
> -        elif defined_in != info.defn_name:
> -            return "%s '%s' of type '%s'" % (role, self.name, defined_in)
> -        return "%s '%s'" % (role, self.name)
> +
> +        return "%s '%s' of type '%s'" % (role, self.name, defined_in)
>  
>  
>  class QAPISchemaEnumMember(QAPISchemaMember):
> diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err
> index baeb3f66d1..6c12358a88 100644
> --- a/tests/qapi-schema/alternate-any.err
> +++ b/tests/qapi-schema/alternate-any.err
> @@ -1,2 +1,2 @@
>  alternate-any.json: In alternate 'Alt':
> -alternate-any.json:2: branch 'one' cannot use built-in type 'any'
> +alternate-any.json:2: branch 'one' of type 'Alt' cannot use built-in type 'any'

Redundant with the "In alternate" context line.  I don't like it, but if
that was all I don't like, I'd be willing to accept it to get PATCH 3
over the hump.

> diff --git a/tests/qapi-schema/alternate-conflict-bool-string.err b/tests/qapi-schema/alternate-conflict-bool-string.err
> index 59ff5efa87..d7fd625632 100644
> --- a/tests/qapi-schema/alternate-conflict-bool-string.err
> +++ b/tests/qapi-schema/alternate-conflict-bool-string.err
> @@ -1,2 +1,2 @@
>  alternate-conflict-bool-string.json: In alternate 'Alt':
> -alternate-conflict-bool-string.json:2: branch 'two' can't be distinguished from 'one'
> +alternate-conflict-bool-string.json:2: branch 'two' of type 'Alt' can't be distinguished from 'one'
> diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
> index d4970284ba..05174ab4f1 100644
> --- a/tests/qapi-schema/alternate-conflict-dict.err
> +++ b/tests/qapi-schema/alternate-conflict-dict.err
> @@ -1,2 +1,2 @@
>  alternate-conflict-dict.json: In alternate 'Alt':
> -alternate-conflict-dict.json:6: branch 'two' can't be distinguished from 'one'
> +alternate-conflict-dict.json:6: branch 'two' of type 'Alt' can't be distinguished from 'one'
> diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.err b/tests/qapi-schema/alternate-conflict-enum-bool.err
> index 5f35855274..02ed66b0bf 100644
> --- a/tests/qapi-schema/alternate-conflict-enum-bool.err
> +++ b/tests/qapi-schema/alternate-conflict-enum-bool.err
> @@ -1,2 +1,2 @@
>  alternate-conflict-enum-bool.json: In alternate 'Alt':
> -alternate-conflict-enum-bool.json:4: branch 'two' can't be distinguished from 'one'
> +alternate-conflict-enum-bool.json:4: branch 'two' of type 'Alt' can't be distinguished from 'one'
> diff --git a/tests/qapi-schema/alternate-conflict-enum-int.err b/tests/qapi-schema/alternate-conflict-enum-int.err
> index 6a6d156664..1874558f49 100644
> --- a/tests/qapi-schema/alternate-conflict-enum-int.err
> +++ b/tests/qapi-schema/alternate-conflict-enum-int.err
> @@ -1,2 +1,2 @@
>  alternate-conflict-enum-int.json: In alternate 'Alt':
> -alternate-conflict-enum-int.json:4: branch 'two' can't be distinguished from 'one'
> +alternate-conflict-enum-int.json:4: branch 'two' of type 'Alt' can't be distinguished from 'one'
> diff --git a/tests/qapi-schema/alternate-conflict-lists.err b/tests/qapi-schema/alternate-conflict-lists.err
> index f3374ec1e7..de480dc86b 100644
> --- a/tests/qapi-schema/alternate-conflict-lists.err
> +++ b/tests/qapi-schema/alternate-conflict-lists.err
> @@ -1,2 +1,2 @@
>  alternate-conflict-lists.json: In alternate 'Alt':
> -alternate-conflict-lists.json:4: branch 'two' can't be distinguished from 'one'
> +alternate-conflict-lists.json:4: branch 'two' of type 'Alt' can't be distinguished from 'one'
> diff --git a/tests/qapi-schema/alternate-conflict-num-string.err b/tests/qapi-schema/alternate-conflict-num-string.err
> index 38c805ea1f..0d21ec590c 100644
> --- a/tests/qapi-schema/alternate-conflict-num-string.err
> +++ b/tests/qapi-schema/alternate-conflict-num-string.err
> @@ -1,2 +1,2 @@
>  alternate-conflict-num-string.json: In alternate 'Alt':
> -alternate-conflict-num-string.json:2: branch 'two' can't be distinguished from 'one'
> +alternate-conflict-num-string.json:2: branch 'two' of type 'Alt' can't be distinguished from 'one'
> diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
> index 2fa08193db..97097cae97 100644
> --- a/tests/qapi-schema/alternate-conflict-string.err
> +++ b/tests/qapi-schema/alternate-conflict-string.err
> @@ -1,2 +1,2 @@
>  alternate-conflict-string.json: In alternate 'Alt':
> -alternate-conflict-string.json:2: branch 'two' can't be distinguished from 'one'
> +alternate-conflict-string.json:2: branch 'two' of type 'Alt' can't be distinguished from 'one'
> diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
> index 3ae9cd2f11..5603aa95e3 100644
> --- a/tests/qapi-schema/alternate-nested.err
> +++ b/tests/qapi-schema/alternate-nested.err
> @@ -1,2 +1,2 @@
>  alternate-nested.json: In alternate 'Alt2':
> -alternate-nested.json:4: branch 'nested' cannot use alternate type 'Alt1'
> +alternate-nested.json:4: branch 'nested' of type 'Alt2' cannot use alternate type 'Alt1'
> diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
> index 17fec1cd17..befd207a76 100644
> --- a/tests/qapi-schema/alternate-unknown.err
> +++ b/tests/qapi-schema/alternate-unknown.err
> @@ -1,2 +1,2 @@
>  alternate-unknown.json: In alternate 'Alt':
> -alternate-unknown.json:2: branch 'unknown' uses unknown type 'MissingType'
> +alternate-unknown.json:2: branch 'unknown' of type 'Alt' uses unknown type 'MissingType'

Redundant the same way in all of the test cases so far.

> diff --git a/tests/qapi-schema/args-member-unknown.err b/tests/qapi-schema/args-member-unknown.err
> index 96b6e5d289..b24c2ae572 100644
> --- a/tests/qapi-schema/args-member-unknown.err
> +++ b/tests/qapi-schema/args-member-unknown.err
> @@ -1,2 +1,2 @@
>  args-member-unknown.json: In command 'oops':
> -args-member-unknown.json:2: parameter 'member' uses unknown type 'NoSuchType'
> +args-member-unknown.json:2: parameter 'member' of type 'oops-arg' uses unknown type 'NoSuchType'

This is args-member-unknown.json:

   # we reject data if it does not contain a known type
   { 'command': 'oops', 'data': { 'member': 'NoSuchType' } }

There is no type 'oops-arg'.  Confusing.

'oops-arg' is in fact the argument type implicitly defined by
{ 'member': 'NoSuchType' }.

> diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
> index e4eb102ae2..0ede36d68e 100644
> --- a/tests/qapi-schema/enum-clash-member.err
> +++ b/tests/qapi-schema/enum-clash-member.err
> @@ -1,2 +1,2 @@
>  enum-clash-member.json: In enum 'MyEnum':
> -enum-clash-member.json:3: value 'one_two' collides with value 'one-two'
> +enum-clash-member.json:3: value 'one_two' of type 'MyEnum' collides with value 'one-two' of type 'MyEnum'
> diff --git a/tests/qapi-schema/features-duplicate-name.err b/tests/qapi-schema/features-duplicate-name.err
> index 0adbee6b0a..ffd124f5b0 100644
> --- a/tests/qapi-schema/features-duplicate-name.err
> +++ b/tests/qapi-schema/features-duplicate-name.err
> @@ -1,2 +1,2 @@
>  features-duplicate-name.json: In struct 'FeatureStruct0':
> -features-duplicate-name.json:1: feature 'foo' collides with feature 'foo'
> +features-duplicate-name.json:1: feature 'foo' of type 'FeatureStruct0' collides with feature 'foo' of type 'FeatureStruct0'
> diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err
> index 79879681d9..632f78b6c0 100644
> --- a/tests/qapi-schema/struct-base-clash-deep.err
> +++ b/tests/qapi-schema/struct-base-clash-deep.err
> @@ -1,2 +1,2 @@
>  struct-base-clash-deep.json: In struct 'Sub':
> -struct-base-clash-deep.json:10: member 'name' collides with member 'name' of type 'Base'
> +struct-base-clash-deep.json:10: member 'name' of type 'Sub' collides with member 'name' of type 'Base'
> diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err
> index 46473947e6..1d54079c16 100644
> --- a/tests/qapi-schema/struct-base-clash.err
> +++ b/tests/qapi-schema/struct-base-clash.err
> @@ -1,2 +1,2 @@
>  struct-base-clash.json: In struct 'Sub':
> -struct-base-clash.json:5: member 'name' collides with member 'name' of type 'Base'
> +struct-base-clash.json:5: member 'name' of type 'Sub' collides with member 'name' of type 'Base'
> diff --git a/tests/qapi-schema/struct-member-name-clash.err b/tests/qapi-schema/struct-member-name-clash.err
> index 7e53a605d2..ebf66cd5b8 100644
> --- a/tests/qapi-schema/struct-member-name-clash.err
> +++ b/tests/qapi-schema/struct-member-name-clash.err
> @@ -1,2 +1,2 @@
>  struct-member-name-clash.json: In struct 'Oops':
> -struct-member-name-clash.json:5: member 'a_b' collides with member 'a-b'
> +struct-member-name-clash.json:5: member 'a_b' of type 'Oops' collides with member 'a-b' of type 'Oops'

More test cases where it's redundant.

> diff --git a/tests/qapi-schema/union-bad-base.err b/tests/qapi-schema/union-bad-base.err
> index 42b2ed1dda..9f92b35a07 100644
> --- a/tests/qapi-schema/union-bad-base.err
> +++ b/tests/qapi-schema/union-bad-base.err
> @@ -1,2 +1,2 @@
>  union-bad-base.json: In union 'TestUnion':
> -union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string'
> +union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string' of type 'TestUnion-base'

This is union-bad-base.json:

  # we allow anonymous base, but enforce no duplicate keys
  { 'enum': 'TestEnum',
    'data': [ 'value1', 'value2' ] }
  { 'struct': 'TestTypeA',
    'data': { 'string': 'str' } }
  { 'struct': 'TestTypeB',
    'data': { 'integer': 'int' } }
  { 'union': 'TestUnion',
    'base': { 'enum1': 'TestEnum', 'string': 'str' },
    'discriminator': 'enum1',
    'data': { 'value1': 'TestTypeA',
              'value2': 'TestTypeB' } }

There is no type 'TestUnion-base'.  Confusing.

'TestUnion-base' is in fact the base type implicitly defined by
{ 'enum1': 'TestEnum', 'string': 'str' }.

> diff --git a/tests/qapi-schema/union-int-branch.err b/tests/qapi-schema/union-int-branch.err
> index 8fdc81edd1..302e3976e0 100644
> --- a/tests/qapi-schema/union-int-branch.err
> +++ b/tests/qapi-schema/union-int-branch.err
> @@ -1,2 +1,2 @@
>  union-int-branch.json: In union 'TestUnion':
> -union-int-branch.json:8: branch 'value1' cannot use built-in type 'int'
> +union-int-branch.json:8: branch 'value1' of type 'TestUnion' cannot use built-in type 'int'
> diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
> index dad79beae0..e60ab9421a 100644
> --- a/tests/qapi-schema/union-unknown.err
> +++ b/tests/qapi-schema/union-unknown.err
> @@ -1,2 +1,2 @@
>  union-unknown.json: In union 'Union':
> -union-unknown.json:3: branch 'unknown' uses unknown type 'MissingType'
> +union-unknown.json:3: branch 'unknown' of type 'Union' uses unknown type 'MissingType'

Redundant again in these two.

No test case demonstrates the commit message's claim "in some cases it
will help to reduce ambiguity".  PATCH 3 adds one:
union-invalid-union-subfield.  It also adds another confusing one:
union-invalid-union-subtype.


Let's have a closer look at QAPISchemaMember.describe() before the patch:

    def describe(self, info):
        role = self.role

@role is 'member' when self is a QAPISchemaMember, 'value' when it's a
QAPISchemaEnumMember, 'feature' when it's a QAPISchemaFeature, and
'branch' when it's a QAPISchemaVariant.

        defined_in = self.defined_in

This is the name of the thing where this QAPISchemaMember is defined.
When object type T inherits member M from B, it's B's name, not T's.

        assert defined_in

        if defined_in.startswith('q_obj_'):

It's a member of an implicitly defined object type.

Special treatment to avoid exposing the made-up names of implicitly
defined object types to the user:

            # See QAPISchema._make_implicit_object_type() - reverse the
            # mapping there to create a nice human-readable description
            defined_in = defined_in[6:]
            if defined_in.endswith('-arg'):
                # Implicit type created for a command's dict 'data'

Defined in a command's implicit argument type (you get that when the
value of 'data' is { ... }).

                assert role == 'member'
                role = 'parameter'

@defined_in already got adjusted to the command name.  Adjust role.

            elif defined_in.endswith('-base'):
                # Implicit type created for a union's dict 'base'

Defined in a union's implicit base type (you get that when the value of
'base' is { ... }).

                role = 'base ' + role

@defined_in already got adjusted to the union name.  Adjust role.

            else:
                assert False

Explodes when we add another kind of implicitly defined object type and
forget to cover it here.

        elif defined_in != info.defn_name:

Not a member of an implicit object type, and @defined_in is not in the
context line "FILE: In META 'NAME':" printed by
QAPISourceInfo.__str__(), typically via.  QAPISourceError.__str__() from
main()'s exception handler for QAPIError.

Use format "ROLE 'MEMBER-NAME' of type 'DEFINED-IN'":

            return "%s '%s' of type '%s'" % (role, self.name, defined_in)

Either a member of an implicit object type, or @defined_in is in the
context line.

Description is "ROLE 'MEMBER-NAME':

        return "%s '%s'" % (role, self.name)

The patch ditches the second format, exposing @defined_in to the user
even when it's the name of an implicitly defined object type.

Before we discuss how to avoid that, let's take a step back and examine
the problem we're trying to solve.


PATCH 3 permits union branches to be unions.  It adds test case
union-invalid-union-subtype.json:

    # 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' } }

Without this patch, the clash is reported like this:

    union-invalid-union-subtype.json: In union 'TestUnion':
    union-invalid-union-subtype.json:25: base member 'type' collides with base member 'type'

Crap.

Comes from QAPISchemaMember.check_clash():

            raise QAPISemError(
                info,
                "%s collides with %s"
                % (self.describe(info), seen[cname].describe(info)))

@self is member 'type' of TestTypeA's base, and seen[cname] is member
'type' of TestUnion's base.

What we'd like to see instead is something like "base member 'type' of
type 'TestTypeA' collides with base member 'type'".

Here's my attempt to get us this message:

    def describe(self, info):
        role = self.role
        meta = 'type'
        defined_in = self.defined_in
        assert defined_in

        if defined_in.startswith('q_obj_'):
            # See QAPISchema._make_implicit_object_type() - reverse the
            # mapping there to create a nice human-readable description
            defined_in = defined_in[6:]
            if defined_in.endswith('-arg'):
                # Implicit type created for a command's dict 'data'
                assert role == 'member'
                role = 'parameter'
                meta = 'command'
                defined_in = defined_in[:-4]
            elif defined_in.endswith('-base'):
                # Implicit type created for a union's dict 'base'
                role = 'base ' + role
                defined_in = defined_in[:-5]
            else:
                assert False

        if defined_in != info.defn_name:
            return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
        return "%s '%s'" % (role, self.name)

No change to output of existing tests.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 3/3] qapi: allow unions to contain further unions
  2023-02-23 13:40 ` [PATCH v2 3/3] qapi: allow unions to contain further unions Daniel P. Berrangé
  2023-03-07  3:53   ` Het Gala
@ 2023-03-17 15:48   ` Markus Armbruster
  1 sibling, 0 replies; 13+ messages in thread
From: Markus Armbruster @ 2023-03-17 15:48 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, Eric Blake, Michael Roth, Het Gala

Daniel P. Berrangé <berrange@redhat.com> writes:

> This extends the QAPI schema validation to permit unions inside unions,
> provided the checks for clashing fields pass.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
>  scripts/qapi/schema.py                        |  6 +-
>  tests/qapi-schema/meson.build                 |  2 +
>  tests/qapi-schema/qapi-schema-test.json       | 32 ++++++++++
>  tests/qapi-schema/qapi-schema-test.out        | 29 ++++++++++
>  .../union-invalid-union-subfield.err          |  2 +
>  .../union-invalid-union-subfield.json         | 30 ++++++++++
>  .../union-invalid-union-subfield.out          |  0
>  .../union-invalid-union-subtype.err           |  2 +
>  .../union-invalid-union-subtype.json          | 29 ++++++++++
>  .../union-invalid-union-subtype.out           |  0
>  tests/unit/test-qobject-input-visitor.c       | 47 +++++++++++++++
>  tests/unit/test-qobject-output-visitor.c      | 58 +++++++++++++++++++
>  12 files changed, 234 insertions(+), 3 deletions(-)
>  create mode 100644 tests/qapi-schema/union-invalid-union-subfield.err
>  create mode 100644 tests/qapi-schema/union-invalid-union-subfield.json
>  create mode 100644 tests/qapi-schema/union-invalid-union-subfield.out
>  create mode 100644 tests/qapi-schema/union-invalid-union-subtype.err
>  create mode 100644 tests/qapi-schema/union-invalid-union-subtype.json
>  create mode 100644 tests/qapi-schema/union-invalid-union-subtype.out
>
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index 6c481ab0c0..5c4457f789 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -465,9 +465,10 @@ def check(self, schema):
>      # on behalf of info, which is not necessarily self.info
>      def check_clash(self, info, seen):
>          assert self._checked
> -        assert not self.variants       # not implemented
>          for m in self.members:
>              m.check_clash(info, seen)
> +        if self.variants:
> +            self.variants.check_clash(info, seen)
>  
>      def connect_doc(self, doc=None):
>          super().connect_doc(doc)
> @@ -652,8 +653,7 @@ def check(self, schema, seen):
>                          self.info,
>                          "branch '%s' is not a value of %s"
>                          % (v.name, self.tag_member.type.describe()))
> -                if (not isinstance(v.type, QAPISchemaObjectType)
> -                        or v.type.variants):
> +                if not isinstance(v.type, QAPISchemaObjectType):
>                      raise QAPISemError(
>                          self.info,
>                          "%s cannot use %s"

I stared at the code some to convince myself this is complete.

> diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build
> index d85b14f28c..1591eb322b 100644
> --- a/tests/qapi-schema/meson.build
> +++ b/tests/qapi-schema/meson.build
> @@ -194,6 +194,8 @@ schemas = [
>    'union-invalid-data.json',
>    'union-invalid-discriminator.json',
>    'union-invalid-if-discriminator.json',
> +  'union-invalid-union-subfield.json',
> +  'union-invalid-union-subtype.json',
>    'union-no-base.json',
>    'union-optional-discriminator.json',
>    'union-string-discriminator.json',
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index ba7302f42b..40f1a3d88d 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -114,6 +114,38 @@
>  { 'struct': 'UserDefC',
>    '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
>  { 'alternate': 'AltEnumBool', 'data': { 'e': 'EnumOne', 'b': 'bool' } }
>  { 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } }
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 043d75c655..9fe1038944 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -105,6 +105,35 @@ alternate UserDefAlternate
>  object UserDefC
>      member string1: 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
>      tag type
>      case e: EnumOne

Looks good to me.  I also inspected the generated code; no complaints.

> diff --git a/tests/qapi-schema/union-invalid-union-subfield.err b/tests/qapi-schema/union-invalid-union-subfield.err
> new file mode 100644
> index 0000000000..43574dea79
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subfield.err
> @@ -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' of type 'TestUnion-base'

Bad error message, see my review of PATCH 1.

> diff --git a/tests/qapi-schema/union-invalid-union-subfield.json b/tests/qapi-schema/union-invalid-union-subfield.json
> new file mode 100644
> index 0000000000..e1639d3a96
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subfield.json
> @@ -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' } }
> diff --git a/tests/qapi-schema/union-invalid-union-subfield.out b/tests/qapi-schema/union-invalid-union-subfield.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/union-invalid-union-subtype.err b/tests/qapi-schema/union-invalid-union-subtype.err
> new file mode 100644
> index 0000000000..e45f330cec
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subtype.err
> @@ -0,0 +1,2 @@
> +union-invalid-union-subtype.json: In union 'TestUnion':
> +union-invalid-union-subtype.json:25: base member 'type' of type 'TestTypeA-base' collides with base member 'type' of type 'TestUnion-base'

Likewise.

> diff --git a/tests/qapi-schema/union-invalid-union-subtype.json b/tests/qapi-schema/union-invalid-union-subtype.json
> new file mode 100644
> index 0000000000..ce1de51d8d
> --- /dev/null
> +++ b/tests/qapi-schema/union-invalid-union-subtype.json
> @@ -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' } }
> diff --git a/tests/qapi-schema/union-invalid-union-subtype.out b/tests/qapi-schema/union-invalid-union-subtype.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c
> index 77fbf985be..9b3e2dbe14 100644
> --- a/tests/unit/test-qobject-input-visitor.c
> +++ b/tests/unit/test-qobject-input-visitor.c
> @@ -706,6 +706,51 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
>      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,
>                                        const void *unused)
>  {
> @@ -1216,6 +1261,8 @@ int main(int argc, char **argv)
>                             NULL, test_visitor_in_null);
>      input_visitor_test_add("/visitor/input/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",
>                             NULL, test_visitor_in_alternate);
>      input_visitor_test_add("/visitor/input/errors",
> diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c
> index 7f054289fe..1535b3ad17 100644
> --- a/tests/unit/test-qobject-output-visitor.c
> +++ b/tests/unit/test-qobject-output-visitor.c
> @@ -352,6 +352,62 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
>      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,
>                                         const void *unused)
>  {
> @@ -586,6 +642,8 @@ int main(int argc, char **argv)
>                              &out_visitor_data, test_visitor_out_list_qapi_free);
>      output_visitor_test_add("/visitor/output/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",
>                              &out_visitor_data, test_visitor_out_alternate);
>      output_visitor_test_add("/visitor/output/null",

Reviewed-by: Markus Armbruster <armbru@redhat.com>



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 0/3] qapi: allow unions to contain further unions
  2023-02-23 13:40 [PATCH v2 0/3] qapi: allow unions to contain further unions Daniel P. Berrangé
                   ` (2 preceding siblings ...)
  2023-02-23 13:40 ` [PATCH v2 3/3] qapi: allow unions to contain further unions Daniel P. Berrangé
@ 2023-03-17 15:55 ` Markus Armbruster
  2023-03-31 11:49   ` Het Gala
  3 siblings, 1 reply; 13+ messages in thread
From: Markus Armbruster @ 2023-03-17 15:55 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, Eric Blake, Michael Roth, Het Gala

Daniel P. Berrangé <berrange@redhat.com> writes:

> Currently it is not possible for a union type to contain a
> further union as one (or more) of its branches. This relaxes
> that restriction and adds the calls needed to validate field
> name uniqueness as unions are flattened.

I apologize for the long delay.  Sick child, sick me, much snot, little
sleep.

PATCH 1 is wrong, but I was able to figure out what's going on there,
and suggested a patch that hopefully works.

PATCH 2 is okay.  I suggested a few tweaks.  I'd put it first, but
that's up to you.

PATCH 3 looks good.

Looking forward to v3.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 0/3] qapi: allow unions to contain further unions
  2023-03-17 15:55 ` [PATCH v2 0/3] " Markus Armbruster
@ 2023-03-31 11:49   ` Het Gala
  2023-04-14  6:32     ` Het Gala
  0 siblings, 1 reply; 13+ messages in thread
From: Het Gala @ 2023-03-31 11:49 UTC (permalink / raw)
  To: Markus Armbruster, Daniel P. Berrangé
  Cc: qemu-devel, Eric Blake, Michael Roth

Hi all,

On 17/03/23 9:25 pm, Markus Armbruster wrote:
> Daniel P. Berrangé <berrange@redhat.com> writes:
>
>> Currently it is not possible for a union type to contain a
>> further union as one (or more) of its branches. This relaxes
>> that restriction and adds the calls needed to validate field
>> name uniqueness as unions are flattened.
> I apologize for the long delay.  Sick child, sick me, much snot, little
> sleep.
>
> PATCH 1 is wrong, but I was able to figure out what's going on there,
> and suggested a patch that hopefully works.
>
> PATCH 2 is okay.  I suggested a few tweaks.  I'd put it first, but
> that's up to you.
>
> PATCH 3 looks good.
>
> Looking forward to v3.

Thankyou Markus for your suggestions and I hope everyone is in good 
health now. This is just a friendly reminder if Daniel is ready with v3 
patches for the same :)

Regards,
Het Gala


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 0/3] qapi: allow unions to contain further unions
  2023-03-31 11:49   ` Het Gala
@ 2023-04-14  6:32     ` Het Gala
  2023-04-25 13:29       ` Markus Armbruster
  0 siblings, 1 reply; 13+ messages in thread
From: Het Gala @ 2023-04-14  6:32 UTC (permalink / raw)
  To: Markus Armbruster, Daniel P. Berrangé
  Cc: qemu-devel, Eric Blake, Michael Roth


On 31/03/23 5:19 pm, Het Gala wrote:
> Hi all,
>
> On 17/03/23 9:25 pm, Markus Armbruster wrote:
>> Daniel P. Berrangé <berrange@redhat.com> writes:
>>
>>> Currently it is not possible for a union type to contain a
>>> further union as one (or more) of its branches. This relaxes
>>> that restriction and adds the calls needed to validate field
>>> name uniqueness as unions are flattened.
>> I apologize for the long delay.  Sick child, sick me, much snot, little
>> sleep.
>>
>> PATCH 1 is wrong, but I was able to figure out what's going on there,
>> and suggested a patch that hopefully works.
>>
>> PATCH 2 is okay.  I suggested a few tweaks.  I'd put it first, but
>> that's up to you.
>>
>> PATCH 3 looks good.
>>
>> Looking forward to v3.
>
> Thankyou Markus for your suggestions and I hope everyone is in good 
> health now. This is just a friendly reminder if Daniel is ready with 
> v3 patches for the same :)
>
> Regards,
> Het Gala

Hi, this is just a reminder mail to check if Daniel has plan to post v3 
patches in the coming days. Would like these patches to get merged in 
qemu as soon as possible, so that we all can focus on restructuring of 
'migrate' QAPI :)

Regards,
Het Gala


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 0/3] qapi: allow unions to contain further unions
  2023-04-14  6:32     ` Het Gala
@ 2023-04-25 13:29       ` Markus Armbruster
  0 siblings, 0 replies; 13+ messages in thread
From: Markus Armbruster @ 2023-04-25 13:29 UTC (permalink / raw)
  To: Het Gala; +Cc: Daniel P. Berrangé, qemu-devel, Eric Blake, Michael Roth

Het Gala <het.gala@nutanix.com> writes:

> Hi, this is just a reminder mail to check if Daniel has plan to post v3 patches in the coming days. Would like these patches to get merged in qemu as soon as possible, so that we all can focus on restructuring of 'migrate' QAPI :)

I just queued v3.  Expect a pull request today or tomorrow.



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2023-04-25 13:29 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-02-23 13:40 [PATCH v2 0/3] qapi: allow unions to contain further unions Daniel P. Berrangé
2023-02-23 13:40 ` [PATCH v2 1/3] qapi: improve specificity of type/member descriptions Daniel P. Berrangé
2023-03-17 12:08   ` Markus Armbruster
2023-02-23 13:40 ` [PATCH v2 2/3] qapi: use env var to trigger qapi test output updates Daniel P. Berrangé
2023-02-24 19:28   ` Eric Blake
2023-03-17 12:05   ` Markus Armbruster
2023-02-23 13:40 ` [PATCH v2 3/3] qapi: allow unions to contain further unions Daniel P. Berrangé
2023-03-07  3:53   ` Het Gala
2023-03-17 15:48   ` Markus Armbruster
2023-03-17 15:55 ` [PATCH v2 0/3] " Markus Armbruster
2023-03-31 11:49   ` Het Gala
2023-04-14  6:32     ` Het Gala
2023-04-25 13:29       ` Markus Armbruster

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).