From 6a8c0b51025314cdb1a8b4be24d45e690f1217dd Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 6 Jun 2019 17:37:57 +0200 Subject: qapi: Add feature flags to struct types Sometimes, the behaviour of QEMU changes without a change in the QMP syntax (usually by allowing values or operations that previously resulted in an error). QMP clients may still need to know whether they can rely on the changed behavior. Let's add feature flags to the QAPI schema language, so that we can make such changes visible with schema introspection. An example for a schema definition using feature flags looks like this: { 'struct': 'TestType', 'data': { 'number': 'int' }, 'features': [ 'allow-negative-numbers' ] } Introspection information then looks like this: { "name": "TestType", "meta-type": "object", "members": [ { "name": "number", "type": "int" } ], "features": [ "allow-negative-numbers" ] } This patch implements feature flags only for struct types. We'll implement them more widely as needed. Signed-off-by: Kevin Wolf Message-Id: <20190606153803.5278-2-armbru@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- tests/qapi-schema/double-type.err | 2 +- tests/qapi-schema/test-qapi.py | 3 ++- tests/qapi-schema/unknown-expr-key.err | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err index 799193d..6945717 100644 --- a/tests/qapi-schema/double-type.err +++ b/tests/qapi-schema/double-type.err @@ -1,2 +1,2 @@ tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar' -Valid keys are 'base', 'data', 'if', 'struct'. +Valid keys are 'base', 'data', 'features', 'if', 'struct'. diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index d21fca0..f2d6815 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -38,7 +38,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('array %s %s' % (name, element_type.name)) self._print_if(ifcond) - def visit_object_type(self, name, info, ifcond, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants, + features): print('object %s' % name) if base: print(' base %s' % base.name) diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err index 6ff8bb9..4340eaf 100644 --- a/tests/qapi-schema/unknown-expr-key.err +++ b/tests/qapi-schema/unknown-expr-key.err @@ -1,2 +1,2 @@ tests/qapi-schema/unknown-expr-key.json:2: Unknown keys 'bogus', 'phony' in struct 'bar' -Valid keys are 'base', 'data', 'if', 'struct'. +Valid keys are 'base', 'data', 'features', 'if', 'struct'. -- cgit v1.1 From 8aa3a33e442fc49fd67e4c8df8869d257c41c77e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 6 Jun 2019 17:37:58 +0200 Subject: tests/qapi-schema: Test for good feature lists in structs Signed-off-by: Kevin Wolf Message-Id: <20190606153803.5278-3-armbru@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- tests/qapi-schema/qapi-schema-test.json | 39 ++++++++++++++++++++++++++++++ tests/qapi-schema/qapi-schema-test.out | 43 +++++++++++++++++++++++++++++++++ tests/qapi-schema/test-qapi.py | 4 +++ tests/test-qmp-cmds.c | 8 ++++++ 4 files changed, 94 insertions(+) (limited to 'tests') diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 0952c68..c6d59ac 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -242,3 +242,42 @@ { 'foo': 'TestIfStruct', 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } + +# test 'features' for structs + +{ 'struct': 'FeatureStruct0', + 'data': { 'foo': 'int' }, + 'features': [] } +{ 'struct': 'FeatureStruct1', + 'data': { 'foo': 'int' }, + 'features': [ 'feature1' ] } +{ 'struct': 'FeatureStruct2', + 'data': { 'foo': 'int' }, + 'features': [ { 'name': 'feature1' } ] } +{ 'struct': 'FeatureStruct3', + 'data': { 'foo': 'int' }, + 'features': [ 'feature1', 'feature2' ] } +{ 'struct': 'FeatureStruct4', + 'data': { 'namespace-test': 'int' }, + 'features': [ 'namespace-test', 'int', 'name', 'if' ] } + +{ 'struct': 'CondFeatureStruct1', + 'data': { 'foo': 'int' }, + 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'} ] } +{ 'struct': 'CondFeatureStruct2', + 'data': { 'foo': 'int' }, + 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'}, + { 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] } +{ 'struct': 'CondFeatureStruct3', + 'data': { 'foo': 'int' }, + 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', + 'defined(TEST_IF_COND_2)'] } ] } +{ 'command': 'test-features', + 'data': { 'fs0': 'FeatureStruct0', + 'fs1': 'FeatureStruct1', + 'fs2': 'FeatureStruct2', + 'fs3': 'FeatureStruct3', + 'fs4': 'FeatureStruct4', + 'cfs1': 'CondFeatureStruct1', + 'cfs2': 'CondFeatureStruct2', + 'cfs3': 'CondFeatureStruct3' } } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 77fb1e1..85d510b 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -354,3 +354,46 @@ object q_obj_TestIfEvent-arg event TestIfEvent q_obj_TestIfEvent-arg boxed=False if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] +object FeatureStruct0 + member foo: int optional=False +object FeatureStruct1 + member foo: int optional=False + feature feature1 +object FeatureStruct2 + member foo: int optional=False + feature feature1 +object FeatureStruct3 + member foo: int optional=False + feature feature1 + feature feature2 +object FeatureStruct4 + member namespace-test: int optional=False + feature namespace-test + feature int + feature name + feature if +object CondFeatureStruct1 + member foo: int optional=False + feature feature1 + if ['defined(TEST_IF_FEATURE_1)'] +object CondFeatureStruct2 + member foo: int optional=False + feature feature1 + if ['defined(TEST_IF_FEATURE_1)'] + feature feature2 + if ['defined(TEST_IF_FEATURE_2)'] +object CondFeatureStruct3 + member foo: int optional=False + feature feature1 + if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] +object q_obj_test-features-arg + member fs0: FeatureStruct0 optional=False + member fs1: FeatureStruct1 optional=False + member fs2: FeatureStruct2 optional=False + member fs3: FeatureStruct3 optional=False + member fs4: FeatureStruct4 optional=False + member cfs1: CondFeatureStruct1 optional=False + member cfs2: CondFeatureStruct2 optional=False + member cfs3: CondFeatureStruct3 optional=False +command test-features q_obj_test-features-arg -> None + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index f2d6815..b0f770b 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -49,6 +49,10 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(m.ifcond, 8) self._print_variants(variants) self._print_if(ifcond) + if features: + for f in features: + print(' feature %s' % f.name) + self._print_if(f.ifcond, 8) def visit_alternate_type(self, name, info, ifcond, variants): print('alternate %s' % name) diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index f0b95dc..ab389f4 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -43,6 +43,14 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } +void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, + FeatureStruct2 *fs2, FeatureStruct3 *fs3, + FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, + CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, + Error **errp) +{ +} + UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, bool has_udb1, UserDefOne *ud1b, Error **errp) -- cgit v1.1 From a00af404253729e025ccdca107c2bf2a4cff3bf9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 6 Jun 2019 17:37:59 +0200 Subject: tests/qapi-schema: Error case tests for features in structs Signed-off-by: Kevin Wolf Message-Id: <20190606153803.5278-4-armbru@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- tests/Makefile.include | 6 ++++++ tests/qapi-schema/features-bad-type.err | 1 + tests/qapi-schema/features-bad-type.exit | 1 + tests/qapi-schema/features-bad-type.json | 3 +++ tests/qapi-schema/features-bad-type.out | 0 tests/qapi-schema/features-duplicate-name.err | 1 + tests/qapi-schema/features-duplicate-name.exit | 1 + tests/qapi-schema/features-duplicate-name.json | 3 +++ tests/qapi-schema/features-duplicate-name.out | 0 tests/qapi-schema/features-missing-name.err | 1 + tests/qapi-schema/features-missing-name.exit | 1 + tests/qapi-schema/features-missing-name.json | 3 +++ tests/qapi-schema/features-missing-name.out | 0 tests/qapi-schema/features-name-bad-type.err | 1 + tests/qapi-schema/features-name-bad-type.exit | 1 + tests/qapi-schema/features-name-bad-type.json | 3 +++ tests/qapi-schema/features-name-bad-type.out | 0 tests/qapi-schema/features-no-list.err | 1 + tests/qapi-schema/features-no-list.exit | 1 + tests/qapi-schema/features-no-list.json | 3 +++ tests/qapi-schema/features-no-list.out | 0 tests/qapi-schema/features-unknown-key.err | 2 ++ tests/qapi-schema/features-unknown-key.exit | 1 + tests/qapi-schema/features-unknown-key.json | 3 +++ tests/qapi-schema/features-unknown-key.out | 0 25 files changed, 37 insertions(+) create mode 100644 tests/qapi-schema/features-bad-type.err create mode 100644 tests/qapi-schema/features-bad-type.exit create mode 100644 tests/qapi-schema/features-bad-type.json create mode 100644 tests/qapi-schema/features-bad-type.out create mode 100644 tests/qapi-schema/features-duplicate-name.err create mode 100644 tests/qapi-schema/features-duplicate-name.exit create mode 100644 tests/qapi-schema/features-duplicate-name.json create mode 100644 tests/qapi-schema/features-duplicate-name.out create mode 100644 tests/qapi-schema/features-missing-name.err create mode 100644 tests/qapi-schema/features-missing-name.exit create mode 100644 tests/qapi-schema/features-missing-name.json create mode 100644 tests/qapi-schema/features-missing-name.out create mode 100644 tests/qapi-schema/features-name-bad-type.err create mode 100644 tests/qapi-schema/features-name-bad-type.exit create mode 100644 tests/qapi-schema/features-name-bad-type.json create mode 100644 tests/qapi-schema/features-name-bad-type.out create mode 100644 tests/qapi-schema/features-no-list.err create mode 100644 tests/qapi-schema/features-no-list.exit create mode 100644 tests/qapi-schema/features-no-list.json create mode 100644 tests/qapi-schema/features-no-list.out create mode 100644 tests/qapi-schema/features-unknown-key.err create mode 100644 tests/qapi-schema/features-unknown-key.exit create mode 100644 tests/qapi-schema/features-unknown-key.json create mode 100644 tests/qapi-schema/features-unknown-key.out (limited to 'tests') diff --git a/tests/Makefile.include b/tests/Makefile.include index 0cd5f46..db750dd 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -378,6 +378,12 @@ qapi-schema += event-boxed-empty.json qapi-schema += event-case.json qapi-schema += event-member-invalid-dict.json qapi-schema += event-nest-struct.json +qapi-schema += features-bad-type.json +qapi-schema += features-duplicate-name.json +qapi-schema += features-missing-name.json +qapi-schema += features-name-bad-type.json +qapi-schema += features-no-list.json +qapi-schema += features-unknown-key.json qapi-schema += flat-union-array-branch.json qapi-schema += flat-union-bad-base.json qapi-schema += flat-union-bad-discriminator.json diff --git a/tests/qapi-schema/features-bad-type.err b/tests/qapi-schema/features-bad-type.err new file mode 100644 index 0000000..5fb95c2 --- /dev/null +++ b/tests/qapi-schema/features-bad-type.err @@ -0,0 +1 @@ +tests/qapi-schema/features-bad-type.json:1: Feature of struct FeatureStruct0 requires a string name diff --git a/tests/qapi-schema/features-bad-type.exit b/tests/qapi-schema/features-bad-type.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/features-bad-type.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/features-bad-type.json b/tests/qapi-schema/features-bad-type.json new file mode 100644 index 0000000..57db554 --- /dev/null +++ b/tests/qapi-schema/features-bad-type.json @@ -0,0 +1,3 @@ +{ 'struct': 'FeatureStruct0', + 'data': { 'foo': 'int' }, + 'features': [ [ 'a feature cannot be an array' ] ] } diff --git a/tests/qapi-schema/features-bad-type.out b/tests/qapi-schema/features-bad-type.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/features-duplicate-name.err b/tests/qapi-schema/features-duplicate-name.err new file mode 100644 index 0000000..c0a4ccc --- /dev/null +++ b/tests/qapi-schema/features-duplicate-name.err @@ -0,0 +1 @@ +tests/qapi-schema/features-duplicate-name.json:1: 'foo' (feature of FeatureStruct0) collides with 'foo' (feature of FeatureStruct0) diff --git a/tests/qapi-schema/features-duplicate-name.exit b/tests/qapi-schema/features-duplicate-name.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/features-duplicate-name.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/features-duplicate-name.json b/tests/qapi-schema/features-duplicate-name.json new file mode 100644 index 0000000..29358e6 --- /dev/null +++ b/tests/qapi-schema/features-duplicate-name.json @@ -0,0 +1,3 @@ +{ 'struct': 'FeatureStruct0', + 'data': { 'foo': 'int' }, + 'features': [ 'foo', 'bar', 'foo' ] } diff --git a/tests/qapi-schema/features-duplicate-name.out b/tests/qapi-schema/features-duplicate-name.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/features-missing-name.err b/tests/qapi-schema/features-missing-name.err new file mode 100644 index 0000000..4f1d271 --- /dev/null +++ b/tests/qapi-schema/features-missing-name.err @@ -0,0 +1 @@ +tests/qapi-schema/features-missing-name.json:1: Key 'name' is missing from feature of struct FeatureStruct0 diff --git a/tests/qapi-schema/features-missing-name.exit b/tests/qapi-schema/features-missing-name.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/features-missing-name.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/features-missing-name.json b/tests/qapi-schema/features-missing-name.json new file mode 100644 index 0000000..2314f97 --- /dev/null +++ b/tests/qapi-schema/features-missing-name.json @@ -0,0 +1,3 @@ +{ 'struct': 'FeatureStruct0', + 'data': { 'foo': 'int' }, + 'features': [ { 'if': 'defined(NAMELESS_FEATURES)' } ] } diff --git a/tests/qapi-schema/features-missing-name.out b/tests/qapi-schema/features-missing-name.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/features-name-bad-type.err b/tests/qapi-schema/features-name-bad-type.err new file mode 100644 index 0000000..8a3eecb --- /dev/null +++ b/tests/qapi-schema/features-name-bad-type.err @@ -0,0 +1 @@ +tests/qapi-schema/features-name-bad-type.json:1: Feature of struct FeatureStruct0 requires a string name diff --git a/tests/qapi-schema/features-name-bad-type.exit b/tests/qapi-schema/features-name-bad-type.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/features-name-bad-type.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/features-name-bad-type.json b/tests/qapi-schema/features-name-bad-type.json new file mode 100644 index 0000000..b071399 --- /dev/null +++ b/tests/qapi-schema/features-name-bad-type.json @@ -0,0 +1,3 @@ +{ 'struct': 'FeatureStruct0', + 'data': { 'foo': 'int' }, + 'features': [ { 'name': { 'feature-type': 'object' } } ] } diff --git a/tests/qapi-schema/features-name-bad-type.out b/tests/qapi-schema/features-name-bad-type.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/features-no-list.err b/tests/qapi-schema/features-no-list.err new file mode 100644 index 0000000..61ed686 --- /dev/null +++ b/tests/qapi-schema/features-no-list.err @@ -0,0 +1 @@ +tests/qapi-schema/features-no-list.json:1: Struct 'FeatureStruct0' requires an array for 'features' diff --git a/tests/qapi-schema/features-no-list.exit b/tests/qapi-schema/features-no-list.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/features-no-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/features-no-list.json b/tests/qapi-schema/features-no-list.json new file mode 100644 index 0000000..9484fd9 --- /dev/null +++ b/tests/qapi-schema/features-no-list.json @@ -0,0 +1,3 @@ +{ 'struct': 'FeatureStruct0', + 'data': { 'foo': 'int' }, + 'features': 'bar' } diff --git a/tests/qapi-schema/features-no-list.out b/tests/qapi-schema/features-no-list.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/features-unknown-key.err b/tests/qapi-schema/features-unknown-key.err new file mode 100644 index 0000000..a1d6930 --- /dev/null +++ b/tests/qapi-schema/features-unknown-key.err @@ -0,0 +1,2 @@ +tests/qapi-schema/features-unknown-key.json:1: Unknown key 'colour' in feature of struct FeatureStruct0 +Valid keys are 'if', 'name'. diff --git a/tests/qapi-schema/features-unknown-key.exit b/tests/qapi-schema/features-unknown-key.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/features-unknown-key.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/features-unknown-key.json b/tests/qapi-schema/features-unknown-key.json new file mode 100644 index 0000000..134df3b --- /dev/null +++ b/tests/qapi-schema/features-unknown-key.json @@ -0,0 +1,3 @@ +{ 'struct': 'FeatureStruct0', + 'data': { 'foo': 'int' }, + 'features': [ { 'name': 'bar', 'colour': 'red' } ] } diff --git a/tests/qapi-schema/features-unknown-key.out b/tests/qapi-schema/features-unknown-key.out new file mode 100644 index 0000000..e69de29 -- cgit v1.1