Merge tag 'misc-next-pull-request' of https://gitlab.com/berrange/qemu into staging Misc patches for python, iotests, qom, crypt & io * Bump python-qemu-qmp to 0.6.0 * Fix client side anoymous TLS credentials * Fix return value semantics for qio_channel_flush * Add ID validation of internal QOM constructor * Fix ability to create internal QOM objects without a parent * Merge user creatable object constructor into main QOM file * Print reason for skipping I/O tests * Remove redundant meson suits for I/O tests * Add I/O tests in meson for NBD and LUKS * Expose make targets for all block driver I/O tests * Run I/O tests for 10 block drivers in GitLab CI * Fix sudo check for LUKS I/O test * Mark I/O test 185 as flaky * Ensure stable sorting for mtest2make output # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmoMXG4ACgkQvobrtBUQ # T9/LGQ//TsS4Z6jRfyBzH3J9BaVRUdZmtbHgZxQSePGLEwEoAjclZ+TgKVYig9d6 # ra0Q8sKKT92UhC8Uewc2SJJW+HHxpGAlO9heCmNjAvP3DswCim7jR4TdJcVfmc8x # URUX0S3xyQ+OQPOHjM4JsdbkM9k97IIO1JsQwGcAIF9rIcTTuXdTa2BMwg22dinu # Ona03jy5bxJaLm3B5oNDcacZHTkq4yhMDnuyqYSb5wE05btGzVqqIsDRPxDpfabe # rvr5g5GTlsub1aaQGf54YiOlwxvqozqBv1xkZ2681J1TLfbSD+94jbRBaOSvT85l # QXTpvimi5ej7r12TqemfkFsV1kkQnpqTPqVuhjwEeD1suD3wiVzKMqFLYO/5z0QH # d5emUU58WUoFGG11c37Fa0hRmmCXmZGOHIpZOrd6ymxuPz9TDWJ6sBLnv/lgQ6/K # yXE4h2nYfdBHAw1tQqBcDJ3lXQrJMfg2CeBONnZcRiMtslI0VyAVpTPYYjbtnIc3 # 7Ky+5OC6PRe+aCbOXGbJChyQUz7jN5tURV/69n62yK0OzBSY1pN3V7CnC9hYmIMB # OxKukPbaXNvmThC4zHOIFmzQ8VBlDpoFvUx4tOiZu6qeuuSxxn3RIvZAj4Ogw7DV # 4MrlyPvDKQh8cpl+nEDJm312X5yEIXe2CpSd7enhK4erViAJLP8= # =y/m/ # -----END PGP SIGNATURE----- # gpg: Signature made Tue 19 May 2026 08:49:50 EDT # gpg: using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full] # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" [full] # Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E 8E3F BE86 EBB4 1510 4FDF * tag 'misc-next-pull-request' of https://gitlab.com/berrange/qemu: (28 commits) qom: drop user_creatable_add_type method qom: allow object_new_with_prop* to trigger module loading qom: fix ability to create objects without a parent qom: add object_new_with_props_from_qdict qom: move object_set_prop_keyval into object.c qom: have object_set_props_keyval return bool qom: shorten name of object_set_properties_from_keyval qom: make errp last param in methods taking va_list qom: validate ID format when creating objects qom: add trace events for object/property lifecycle gitlab: remove I/O tests from build-tcg-disabled job gitlab: add jobs for thorough block tests iotests: mark 185 as a flaky test iotests: fix check for sudo access in LUKS I/O test iotests: validate dmsetup result in test 128 iotests: use 'driver' as collective term for either format or protocol iotests: add nbd and luks to the I/O test suites docs/devel/testing: expand documentation for 'make check-block' iotests: add a meson suite / make target per block I/O tests format scripts/mtest2make: support optional tests grouping ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index d866cb1..0050586 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml
@@ -1,5 +1,20 @@ -.native_build_job_template: + +# Any job running meson should capture meson logs +# by default. Some jobs might override the artifacts +# to capture further files +.meson_job_template: extends: .base_job_template + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + when: always + expire_in: 7 days + paths: + - build/meson-logs + reports: + junit: build/meson-logs/*.junit.xml + +.native_build_job_template: + extends: .meson_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG cache: @@ -60,7 +75,7 @@ - build/**/*.c.o.d .common_test_job_template: - extends: .base_job_template + extends: .meson_job_template stage: test image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG script: @@ -87,14 +102,7 @@ # Prevent logs from the build job that run earlier # from being duplicated in the test job artifacts - rm -f build/meson-logs/* - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" - when: always - expire_in: 7 days - paths: - - build/meson-logs - reports: - junit: build/meson-logs/*.junit.xml + .functional_test_job_template: extends: .common_test_job_template @@ -125,7 +133,7 @@ QEMU_JOB_FUNCTIONAL: 1 .wasm_build_job_template: - extends: .base_job_template + extends: .meson_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG before_script:
diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 4b1949a..d054349 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml
@@ -174,6 +174,24 @@ x86_64-softmmu rx-softmmu sh4-softmmu MAKE_CHECK_ARGS: check-build + +# NB: block-XXX jobs use 'centos' since that is the build +# job that provides the x86_64-softmmu. Some I/O tests +# are currently buggy and blindly assume characteristics +# of x86 (such as PCIe) causing failures with other arches + +block: + extends: .native_test_job_template + needs: + - job: build-system-centos + artifacts: true + variables: + IMAGE: centos9 + MAKE_CHECK_ARGS: "check-block-$FORMAT" + parallel: + matrix: + - FORMAT: [luks, nbd, parallels, qcow2, qed, raw, vdi, vhdx, vmdk, vpc] + # Previous QEMU release. Used for cross-version migration tests. build-previous-qemu: extends: .native_build_job_template @@ -348,15 +366,6 @@ - make -j"$JOBS" - make check-unit - make check-qapi-schema - - ./run tests/qemu-iotests/check -raw 001 002 003 004 005 008 009 - 010 011 012 021 025 032 033 048 052 063 077 086 101 104 106 - 113 148 150 151 152 157 159 160 163 170 171 184 192 194 208 - 221 226 227 236 253 277 image-fleecing - - ./run tests/qemu-iotests/check -qcow2 028 051 056 057 058 065 068 - 082 085 091 095 096 102 122 124 132 139 142 144 145 151 152 - 155 157 165 194 196 200 202 208 209 216 218 227 234 246 247 - 248 250 254 255 257 258 260 261 262 263 264 270 272 273 277 - 279 image-fleecing - make distclean build-user:
diff --git a/authz/listfile.c b/authz/listfile.c index 13741d5..23655f8 100644 --- a/authz/listfile.c +++ b/authz/listfile.c
@@ -79,8 +79,8 @@ v = qobject_input_visitor_new(obj); - ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST, - NULL, pdict, v, errp); + ret = QAUTHZ(object_new_with_props_from_qdict_parentless( + TYPE_QAUTHZ_LIST, pdict, v, errp)); cleanup: visit_free(v);
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c index 1551382..190c983 100644 --- a/crypto/tlscredsanon.c +++ b/crypto/tlscredsanon.c
@@ -73,6 +73,8 @@ box->dh_params); } } else { + box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_ANON); + ret = gnutls_anon_allocate_client_credentials(&box->data.anonclient); if (ret < 0) { error_setg(errp, "Cannot allocate credentials: %s",
diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index b01a374..c0321d1 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst
@@ -236,9 +236,29 @@ check-block ~~~~~~~~~~~ -``make check-block`` runs a subset of the block layer iotests (the tests that -are in the "auto" group). -See the "QEMU iotests" section below for more information. +There are a variety of ways to exercise the block layer I/O tests +via make targets for a selection of formats / protocols (collectively +referred to as ``drivers`` below). + +A default ``make check`` or ``make check-block`` command will exercise +the ``qcow2`` format, using the tests tagged into the ``auto`` group +only. + +These targets accept the ``SPEED`` variable to augment the set of tests +to run. A slightly more comprehensive test plan can be run by defining +``SPEED=slow``, which enables all tests for the ``qcow2`` and ``raw`` +drivers. The most comprehensive test plan can be run by defining +``SPEED=thorough``, which enables all available tests for the drivers +``luks``, ``nbd``, ``parallels``, ``qcow2``, ``qed``, ``raw``, ``vdi``, +``vhdx``, ``vmdk``, and ``vpc``. + +Each of drivers also has its own dedicated make target, named +``make check-block-$DRIVER`` which will run all available tests for +the designated driver and does not require the ``SPEED`` variable +to be set. + +See the "QEMU iotests" section below for more information on the +block I/O test framework that is leveraged by these ``make`` targets. .. _qemu-iotests:
diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h index a1ef313..b07cd61 100644 --- a/include/io/channel-socket.h +++ b/include/io/channel-socket.h
@@ -50,11 +50,7 @@ ssize_t zero_copy_queued; ssize_t zero_copy_sent; bool blocking; - /** - * This flag indicates whether any new data was successfully sent with - * zerocopy since the last qio_channel_socket_flush() call. - */ - bool new_zero_copy_sent_success; + bool zero_copy_fallback; };
diff --git a/include/io/channel.h b/include/io/channel.h index 287d10c..98485c9 100644 --- a/include/io/channel.h +++ b/include/io/channel.h
@@ -1147,8 +1147,8 @@ * If not implemented, acts as a no-op, and returns 0. * * Returns -1 if any error is found, - * 1 if every send failed to use zero copy. - * 0 otherwise. + * 1 if at least one send failed to use zero copy. + * 0 if every send successfully used zero copy. */ int qio_channel_flush(QIOChannel *ioc,
diff --git a/include/qom/object.h b/include/qom/object.h index 2a28293..11f5561 100644 --- a/include/qom/object.h +++ b/include/qom/object.h
@@ -689,16 +689,82 @@ * @typename: The name of the type of the object to instantiate. * @parent: the parent object * @id: The unique ID of the object - * @errp: pointer to error object * @vargs: list of property names and values + * @errp: pointer to error object * * See object_new_with_props() for documentation. */ Object *object_new_with_propv(const char *typename, Object *parent, const char *id, - Error **errp, - va_list vargs); + va_list vargs, + Error **errp); + +/** + * object_new_with_props_from_qdict: + * @typename: The name of the type of the object to instantiate. + * @parent: the parent object + * @id: The unique ID of the object + * @props: dictionary of property names and values + * @v: visitor to iterate over @props + * @errp: pointer to error object + * + * A variant of object_new_with_props() which accepts the + * properties in a QDict. + */ +Object *object_new_with_props_from_qdict(const char *typename, + Object *parent, + const char *id, + const QDict *props, + Visitor *v, + Error **errp); + +/** + * object_new_with_props_parentless: + * @typename: The name of the type of the object to instantiate. + * @errp: pointer to error object + * @...: list of property names and values + * + * Behaviour as object_new_with_props(), except the object + * will not be added to any parent and thus the caller will + * own the returned instance. The caller must call + * object_unref when it is no longer required. + */ +Object *object_new_with_props_parentless(const char *typename, + Error **errp, + ...) G_GNUC_NULL_TERMINATED; + +/** + * object_new_with_propv_parentless: + * @typename: The name of the type of the object to instantiate. + * @vargs: list of property names and values + * @errp: pointer to error object + * + * Behaviour as object_new_with_propv(), except the object + * will not be added to any parent and thus the caller will + * own the returned instance. The caller must call + * object_unref when it is no longer required. + */ +Object *object_new_with_propv_parentless(const char *typename, + va_list vargs, + Error **errp); + +/** + * object_new_with_props_from_qdict_parentless: + * @typename: The name of the type of the object to instantiate. + * @props: dictionary of property names and values + * @v: visitor to iterate over @props + * @errp: pointer to error object + * + * Behaviour as object_new_with_props_from_qdict(), except the + * object will not be added to any parent and thus the caller + * will own the returned instance. The caller must call + * object_unref when it is no longer required. + */ +Object *object_new_with_props_from_qdict_parentless(const char *typename, + const QDict *props, + Visitor *v, + Error **errp); /** * object_set_props: @@ -739,14 +805,45 @@ /** * object_set_propv: * @obj: the object instance to set properties on - * @errp: pointer to error object * @vargs: list of property names and values + * @errp: pointer to error object * * See object_set_props() for documentation. * * Returns: %true on success, %false on error. */ -bool object_set_propv(Object *obj, Error **errp, va_list vargs); +bool object_set_propv(Object *obj, va_list vargs, Error **errp); + +/** + * object_set_props_from_qdict: + * @obj: a QOM object + * @qdict: a dictionary with the properties to be set + * @v: a visitor to iterate over @dict + * @errp: pointer to error object + * + * For each key in the dictionary, set the corresponding + * property in @obj. + * + * Returns: %true on success, %false on error. + */ +bool object_set_props_from_qdict(Object *obj, const QDict *qdict, + Visitor *v, Error **errp); + +/** + * object_set_props_from_keyval: + * @obj: a QOM object + * @qdict: a dictionary with the properties to be set + * @from_json: true if leaf values of @qdict are typed, false if they + * are strings + * @errp: pointer to error object + * + * For each key in the dictionary, parse the value string if needed, + * then set the corresponding property in @obj. + * + * Returns: %true on success, %false on error. + */ +bool object_set_props_from_keyval(Object *obj, const QDict *qdict, + bool from_json, Error **errp); /** * object_initialize: @@ -915,20 +1012,6 @@ bool type_print_class_properties(const char *type); /** - * object_set_properties_from_keyval: - * @obj: a QOM object - * @qdict: a dictionary with the properties to be set - * @from_json: true if leaf values of @qdict are typed, false if they - * are strings - * @errp: pointer to error object - * - * For each key in the dictionary, parse the value string if needed, - * then set the corresponding property in @obj. - */ -void object_set_properties_from_keyval(Object *obj, const QDict *qdict, - bool from_json, Error **errp); - -/** * object_class_dynamic_cast_assert: * @klass: The #ObjectClass to attempt to cast. * @typename: The QOM typename of the class to cast to.
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 02b11a7..e2b8615 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h
@@ -70,24 +70,6 @@ bool user_creatable_can_be_deleted(UserCreatable *uc); /** - * user_creatable_add_type: - * @type: the object type name - * @id: the unique ID for the object - * @qdict: the object properties - * @v: the visitor - * @errp: if an error occurs, a pointer to an area to store the error - * - * Create an instance of the user creatable object @type, placing - * it in the object composition tree with name @id, initializing - * it with properties from @qdict - * - * Returns: the newly created object or NULL on error - */ -Object *user_creatable_add_type(const char *type, const char *id, - const QDict *qdict, - Visitor *v, Error **errp); - -/** * user_creatable_add_qapi: * @options: the object definition * @errp: if an error occurs, a pointer to an area to store the error
diff --git a/io/channel-socket.c b/io/channel-socket.c index 3053b35..ea2ec84 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c
@@ -72,7 +72,7 @@ sioc->zero_copy_queued = 0; sioc->zero_copy_sent = 0; sioc->blocking = false; - sioc->new_zero_copy_sent_success = false; + sioc->zero_copy_fallback = false; ioc = QIO_CHANNEL(sioc); qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); @@ -880,9 +880,9 @@ /* No errors, count successfully finished sendmsg()*/ sioc->zero_copy_sent += serr->ee_data - serr->ee_info + 1; - /* If any sendmsg() succeeded using zero copy, mark zerocopy success */ - if (serr->ee_code != SO_EE_CODE_ZEROCOPY_COPIED) { - sioc->new_zero_copy_sent_success = true; + if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) { + /* If any sendmsg() fell back to a copy, mark fallback as true */ + sioc->zero_copy_fallback = true; } } @@ -900,12 +900,12 @@ return ret; } - if (sioc->new_zero_copy_sent_success) { - sioc->new_zero_copy_sent_success = false; - return 0; + if (sioc->zero_copy_fallback) { + sioc->zero_copy_fallback = false; + return 1; } - return 1; + return 0; } #endif /* QEMU_MSG_ZEROCOPY */
diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index 7c61afd..1bb59d8 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py
@@ -43,8 +43,8 @@ def main() -> int: packages = { "meson==1.11.1": "9b3a023657e393dbc5335b95c561337d49b7a458f5541e47ec44f2cc566e0d80", - "qemu.qmp==0.0.5": - "e05782d6df5844b34e0d2f7c68693525da074deef7b641c1401dda6e4e3d6303", + "qemu.qmp==0.0.6": + "5d7c5af0e9de427696e3bf72e333965c3a697929f77f6b7ddc30c989fc7b539b", "pycotap==1.3.1": "1c3a25b3ff89e48f4e00f1f71dbbc1642b4f65c65d416524d07e73492fff25ea", }
diff --git a/python/wheels/qemu_qmp-0.0.5-py3-none-any.whl b/python/wheels/qemu_qmp-0.0.6-py3-none-any.whl similarity index 64% rename from python/wheels/qemu_qmp-0.0.5-py3-none-any.whl rename to python/wheels/qemu_qmp-0.0.6-py3-none-any.whl index 6372b75..5754130 100644 --- a/python/wheels/qemu_qmp-0.0.5-py3-none-any.whl +++ b/python/wheels/qemu_qmp-0.0.6-py3-none-any.whl Binary files differ
diff --git a/pythondeps.toml b/pythondeps.toml index 47d4cae..bef88d4 100644 --- a/pythondeps.toml +++ b/pythondeps.toml
@@ -36,7 +36,7 @@ # only include dependencies that can be guaranteed via configure from # system packages, or python packages we vendor. [tooling] -"qemu.qmp" = { accepted = ">=0.0.5", installed = "0.0.5" } +"qemu.qmp" = { accepted = ">=0.0.5", installed = "0.0.6" } "qemu" = { path = "python/" } # NB: The following dependencies should be a little bit more modern than # the versions listed here, but we are still using Debian 11 for several
diff --git a/qom/object.c b/qom/object.c index dfdc5c8..0ac201d 100644 --- a/qom/object.c +++ b/qom/object.c
@@ -23,7 +23,10 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/forward-visitor.h" #include "qapi/qapi-builtin-visit.h" +#include "qobject/qdict.h" #include "qobject/qjson.h" +#include "qemu/id.h" +#include "qapi/qmp/qerror.h" #include "trace.h" /* TODO: replace QObject with a simpler visitor to avoid a dependency @@ -539,7 +542,7 @@ object_initialize(childobj, size, type); obj = OBJECT(childobj); - if (!object_set_propv(obj, errp, vargs)) { + if (!object_set_propv(obj, vargs, errp)) { goto out; } @@ -594,6 +597,8 @@ object_property_iter_init(&iter, obj); while ((prop = object_property_iter_next(&iter)) != NULL) { if (g_hash_table_add(done, prop)) { + trace_object_property_del(obj, obj->class->type->name, + prop->name, prop->opaque); if (prop->release) { prop->release(obj, prop->name, prop->opaque); released = true; @@ -612,10 +617,14 @@ GHashTableIter iter; gpointer key, value; + trace_object_property_del_child(obj, obj->class->type->name, + child, child->class->type->name); g_hash_table_iter_init(&iter, obj->properties); while (g_hash_table_iter_next(&iter, &key, &value)) { prop = value; if (object_property_is_child(prop) && prop->opaque == child) { + trace_object_property_del(obj, obj->class->type->name, + prop->name, prop->opaque); if (prop->release) { prop->release(obj, prop->name, prop->opaque); prop->release = NULL; @@ -655,7 +664,7 @@ { Object *obj = data; TypeImpl *ti = obj->class->type; - + trace_object_finalize(obj, obj->class->type->name); object_property_del_all(obj); object_deinit(obj, ti); @@ -705,6 +714,7 @@ object_initialize_with_type(obj, size, type); obj->free = obj_free; + trace_object_new(obj, obj->class->type->name); return obj; } @@ -730,25 +740,41 @@ va_list vargs; Object *obj; + assert(parent != NULL); + assert(id != NULL); va_start(vargs, errp); - obj = object_new_with_propv(typename, parent, id, errp, vargs); + obj = object_new_with_propv(typename, parent, id, vargs, errp); va_end(vargs); return obj; } -Object *object_new_with_propv(const char *typename, - Object *parent, - const char *id, - Error **errp, - va_list vargs) +static Object * +object_new_with_props_helper(const char *typename, + Object *parent, + const char *id, + void *props, + bool (set_props)(Object *obj, void *props, + Error **errp), + Error **errp) { + ERRP_GUARD(); Object *obj; ObjectClass *klass; UserCreatable *uc; - klass = object_class_by_name(typename); + assert((id != NULL && parent != NULL) || + (id == NULL && parent == NULL)); + + if (id != NULL && !id_wellformed(id)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); + error_append_hint(errp, "Identifiers consist of letters, digits, " + "'-', '.', '_', starting with a letter.\n"); + return NULL; + } + + klass = module_object_class_by_name(typename); if (!klass) { error_setg(errp, "invalid object type: %s", typename); return NULL; @@ -760,12 +786,15 @@ } obj = object_new_with_type(klass->type); - if (!object_set_propv(obj, errp, vargs)) { + if (!set_props(obj, props, errp)) { goto error; } if (id != NULL) { - object_property_add_child(parent, id, obj); + object_property_try_add_child(parent, id, obj, errp); + if (*errp) { + goto error; + } } uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); @@ -778,7 +807,6 @@ } } - object_unref(obj); return obj; error: @@ -786,16 +814,124 @@ return NULL; } +struct ObjectNewVargsData { + va_list vargs; +}; + +static bool object_new_with_propv_setter(Object *obj, + void *props, + Error **errp) +{ + struct ObjectNewVargsData *data = props; + return object_set_propv(obj, data->vargs, errp); +} + +Object *object_new_with_propv(const char *typename, + Object *parent, + const char *id, + va_list vargs, + Error **errp) +{ + Object *obj; + struct ObjectNewVargsData data; + assert(parent != NULL); + assert(id != NULL); + va_copy(data.vargs, vargs); + obj = object_new_with_props_helper(typename, + parent, + id, + &data, + object_new_with_propv_setter, + errp); + va_end(data.vargs); + if (obj) { + object_unref(obj); + } + return obj; +} + +struct ObjectNewQDictData { + const QDict *props; + Visitor *v; +}; + +static bool object_new_with_qdict_setter(Object *obj, + void *props, + Error **errp) +{ + struct ObjectNewQDictData *data = props; + return object_set_props_from_qdict(obj, data->props, data->v, errp); +} + +Object *object_new_with_props_from_qdict(const char *typename, + Object *parent, + const char *id, + const QDict *props, + Visitor *v, + Error **errp) +{ + struct ObjectNewQDictData data = { props, v }; + Object *obj; + assert(parent != NULL); + assert(id != NULL); + obj = object_new_with_props_helper(typename, + parent, + id, + &data, + object_new_with_qdict_setter, + errp); + if (obj) { + object_unref(obj); + } + return obj; +} + +Object *object_new_with_props_parentless(const char *typename, + Error **errp, + ...) +{ + va_list vargs; + Object *obj; + + va_start(vargs, errp); + obj = object_new_with_propv_parentless(typename, vargs, errp); + va_end(vargs); + + return obj; +} + +Object *object_new_with_propv_parentless(const char *typename, + va_list vargs, + Error **errp) +{ + Object *ret; + struct ObjectNewVargsData data; + va_copy(data.vargs, vargs); + ret = object_new_with_props_helper(typename, NULL, NULL, &data, + object_new_with_propv_setter, errp); + va_end(data.vargs); + return ret; +} + +Object *object_new_with_props_from_qdict_parentless(const char *typename, + const QDict *props, + Visitor *v, + Error **errp) +{ + struct ObjectNewQDictData data = { props, v }; + return object_new_with_props_helper(typename, NULL, NULL, &data, + object_new_with_qdict_setter, errp); +} bool object_set_props(Object *obj, - Error **errp, - ...) + Error **errp, + ...) { va_list vargs; bool ret; va_start(vargs, errp); - ret = object_set_propv(obj, errp, vargs); + ret = object_set_propv(obj, vargs, errp); va_end(vargs); return ret; @@ -803,8 +939,8 @@ bool object_set_propv(Object *obj, - Error **errp, - va_list vargs) + va_list vargs, + Error **errp) { const char *propname; @@ -822,6 +958,41 @@ return true; } +bool object_set_props_from_qdict(Object *obj, const QDict *qdict, + Visitor *v, Error **errp) +{ + ERRP_GUARD(); + const QDictEntry *e; + + if (!visit_start_struct(v, NULL, NULL, 0, errp)) { + return false; + } + for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { + if (!object_property_set(obj, e->key, v, errp)) { + goto out; + } + } + visit_check_struct(v, errp); +out: + visit_end_struct(v, NULL); + + return *errp == NULL; +} + +bool object_set_props_from_keyval(Object *obj, const QDict *qdict, + bool from_json, Error **errp) +{ + bool ret; + Visitor *v; + if (from_json) { + v = qobject_input_visitor_new(QOBJECT(qdict)); + } else { + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + } + ret = object_set_props_from_qdict(obj, qdict, v, errp); + visit_free(v); + return ret; +} Object *object_dynamic_cast(Object *obj, const char *typename) { @@ -835,8 +1006,9 @@ Object *object_dynamic_cast_assert(Object *obj, const char *typename, const char *file, int line, const char *func) { - trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", - typename, file, line, func); + trace_object_dynamic_cast_assert( + obj, obj ? obj->class->type->name : "(null)", + typename, file, line, func); #ifdef CONFIG_QOM_CAST_DEBUG int i; @@ -926,8 +1098,9 @@ { ObjectClass *ret; - trace_object_class_dynamic_cast_assert(class ? class->type->name : "(null)", - typename, file, line, func); + trace_object_class_dynamic_cast_assert( + class ? class->type->name : "(null)", + typename, file, line, func); #ifdef CONFIG_QOM_CAST_DEBUG int i; @@ -1211,6 +1384,8 @@ prop->release = release; prop->opaque = opaque; + trace_object_property_add(obj, obj->class->type->name, + prop->name, prop->opaque); g_hash_table_insert(obj->properties, prop->name, prop); return prop; } @@ -1249,6 +1424,8 @@ prop->release = release; prop->opaque = opaque; + trace_object_class_property_add(klass->type->name, prop->name, + prop->opaque); g_hash_table_insert(klass->properties, prop->name, prop); return prop; @@ -1337,6 +1514,8 @@ { ObjectProperty *prop = g_hash_table_lookup(obj->properties, name); + trace_object_property_del(obj, obj->class->type->name, prop->name, + prop->opaque); if (prop->release) { prop->release(obj, name, prop->opaque); } @@ -1625,8 +1804,11 @@ bool object_property_parse(Object *obj, const char *name, const char *string, Error **errp) { - Visitor *v = string_input_visitor_new(string); - bool ok = object_property_set(obj, name, v, errp); + Visitor *v; + bool ok; + trace_object_property_parse(obj, obj->class->type->name, name, string); + v = string_input_visitor_new(string); + ok = object_property_set(obj, name, v, errp); visit_free(v); return ok; @@ -1757,6 +1939,8 @@ g_autofree char *type = NULL; ObjectProperty *op; + trace_object_property_add_child(obj, obj->class->type->name, name, + child, child->class->type->name); assert(!child->parent); type = g_strdup_printf("child<%s>", object_get_typename(child));
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 415cbee..7080f85 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c
@@ -44,106 +44,11 @@ } } -static void object_set_properties_from_qdict(Object *obj, const QDict *qdict, - Visitor *v, Error **errp) -{ - const QDictEntry *e; - - if (!visit_start_struct(v, NULL, NULL, 0, errp)) { - return; - } - for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - if (!object_property_set(obj, e->key, v, errp)) { - goto out; - } - } - visit_check_struct(v, errp); -out: - visit_end_struct(v, NULL); -} - -void object_set_properties_from_keyval(Object *obj, const QDict *qdict, - bool from_json, Error **errp) -{ - Visitor *v; - if (from_json) { - v = qobject_input_visitor_new(QOBJECT(qdict)); - } else { - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - } - object_set_properties_from_qdict(obj, qdict, v, errp); - visit_free(v); -} - -Object *user_creatable_add_type(const char *type, const char *id, - const QDict *qdict, - Visitor *v, Error **errp) -{ - ERRP_GUARD(); - Object *obj; - ObjectClass *klass; - Error *local_err = NULL; - - if (id != NULL && !id_wellformed(id)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); - error_append_hint(errp, "Identifiers consist of letters, digits, " - "'-', '.', '_', starting with a letter.\n"); - return NULL; - } - - klass = module_object_class_by_name(type); - if (!klass) { - error_setg(errp, "invalid object type: %s", type); - return NULL; - } - - if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) { - error_setg(errp, "object type '%s' isn't supported by object-add", - type); - return NULL; - } - - if (object_class_is_abstract(klass)) { - error_setg(errp, "object type '%s' is abstract", type); - return NULL; - } - - assert(qdict); - obj = object_new_with_class(klass); - object_set_properties_from_qdict(obj, qdict, v, &local_err); - if (local_err) { - goto out; - } - - if (id != NULL) { - object_property_try_add_child(object_get_objects_root(), - id, obj, &local_err); - if (local_err) { - goto out; - } - } - - if (!user_creatable_complete(USER_CREATABLE(obj), &local_err)) { - if (id != NULL) { - object_property_del(object_get_objects_root(), id); - } - goto out; - } -out: - if (local_err) { - error_propagate(errp, local_err); - object_unref(obj); - return NULL; - } - return obj; -} - void user_creatable_add_qapi(ObjectOptions *options, Error **errp) { Visitor *v; QObject *qobj; QDict *props; - Object *obj; v = qobject_output_visitor_new(&qobj); visit_type_ObjectOptions(v, NULL, &options, &error_abort); @@ -155,9 +60,9 @@ qdict_del(props, "id"); v = qobject_input_visitor_new(QOBJECT(props)); - obj = user_creatable_add_type(ObjectType_str(options->qom_type), - options->id, props, v, errp); - object_unref(obj); + object_new_with_props_from_qdict(ObjectType_str(options->qom_type), + object_get_objects_root(), + options->id, props, v, errp); qobject_unref(qobj); visit_free(v); }
diff --git a/qom/trace-events b/qom/trace-events index b2e9f4a..44c63e7 100644 --- a/qom/trace-events +++ b/qom/trace-events
@@ -1,5 +1,13 @@ # See docs/devel/tracing.rst for syntax documentation. # object.c -object_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" -object_class_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" +object_dynamic_cast_assert(void *obj, const char *type, const char *target, const char *file, int line, const char *func) "obj=%p type=%s->%s (%s:%d:%s)" +object_finalize(void *obj, const char *type) "obj=%p type=%s" +object_new(void *obj, const char *type) "obj=%p type=%s" +object_property_add(void *obj, const char *type, const char *name, void *value) "obj=%p type=%s name=%s value=%p" +object_property_add_child(void *obj, const char *type, const char *name, void *child, const char *childtype) "obj=%p type=%s name=%s child=%p child-type=%s" +object_property_del(void *obj, const char *type, const char *name, void *value) "obj=%p type=%s name=%s value=%p" +object_property_del_child(void *obj, const char *type, void *child, const char *childtype) "obj=%p type=%s child=%p child-type=%s" +object_property_parse(void *obj, const char *type, const char *name, const char *value) "obj=%p type=%s prop=%s value=%s" +object_class_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "type=%s->%s (%s:%d:%s)" +object_class_property_add(const char *type, const char *name, void *value) "type=%s name=%s value=%p"
diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index 4b252de..383ea68 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py
@@ -22,7 +22,7 @@ def names(self, base): print(r''' SPEED = quick -.speed.quick = $(sort $(filter-out %-slow %-thorough, $1)) +.speed.quick = $(sort $(filter-out %-slow %-thorough %-optional, $1)) .speed.slow = $(sort $(filter-out %-thorough, $1)) .speed.thorough = $(sort $1) @@ -66,9 +66,16 @@ def process_tests(test, targets, suites): s = s[:-9] suites[s].speeds.add('thorough') +def target_name(suite): + if suite.endswith('-optional'): + return suite[0:-9] + return suite + def emit_prolog(suites, prefix): - all_targets = ' '.join((f'{prefix}-{k}' for k in suites.keys())) - all_xml = ' '.join((f'{prefix}-report-{k}.junit.xml' for k in suites.keys())) + all_targets = ' '.join((f'{prefix}-{target_name(k)}' + for k in sorted(suites.keys()))) + all_xml = ' '.join((f'{prefix}-report-{target_name(k)}.junit.xml' + for k in sorted(suites.keys()))) print() print(f'all-{prefix}-targets = {all_targets}') print(f'all-{prefix}-xml = {all_xml}') @@ -81,14 +88,17 @@ def emit_prolog(suites, prefix): print(f'\t$(MAKE) {prefix}$* MTESTARGS="$(MTESTARGS) --logbase {prefix}-report$*" && ln -f meson-logs/$@ .') def emit_suite(name, suite, prefix): - deps = ' '.join(suite.deps) + tgtname = target_name(name) + deps = ' '.join(sorted(suite.deps)) print() - print(f'.{prefix}-{name}.deps = {deps}') - print(f'.ninja-goals.check-build += $(.{prefix}-{name}.deps)') + print(f'.{prefix}-{tgtname}.deps = {deps}') + print(f'.ninja-goals.check-build += $(.{prefix}-{tgtname}.deps)') - names = ' '.join(suite.names(name)) - targets = f'{prefix}-{name} {prefix}-report-{name}.junit.xml' - if not name.endswith('-slow') and not name.endswith('-thorough'): + names = ' '.join(sorted(suite.names(name))) + targets = f'{prefix}-{tgtname} {prefix}-report-{tgtname}.junit.xml' + if not name.endswith('-slow') and \ + not name.endswith('-thorough') and \ + not name.endswith('-optional'): targets += f' {prefix} {prefix}-report.junit.xml' print(f'ifneq ($(filter {targets}, $(MAKECMDGOALS)),)') # for the "base" suite possibly add FOO-slow and FOO-thorough
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index e5b55e3..dfc95a0 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c
@@ -730,8 +730,8 @@ qdict_del(properties, "bus"); qdict_del(properties, "id"); - object_set_properties_from_keyval(&dev->parent_obj, properties, from_json, - errp); + object_set_props_from_keyval(&dev->parent_obj, properties, from_json, + errp); qobject_unref(properties); if (*errp) { goto err_del_dev;
diff --git a/system/vl.c b/system/vl.c index e690aa3..da36b2c 100644 --- a/system/vl.c +++ b/system/vl.c
@@ -2010,7 +2010,8 @@ static void qemu_apply_machine_options(QDict *qdict) { - object_set_properties_from_keyval(OBJECT(current_machine), qdict, false, &error_fatal); + object_set_props_from_keyval(OBJECT(current_machine), qdict, + false, &error_fatal); if (semihosting_enabled(false) && !semihosting_get_argc()) { /* fall back to the -kernel/-append */ @@ -2225,8 +2226,8 @@ keyval_parse(machine_class->default_machine_opts, NULL, NULL, &error_abort); qemu_apply_legacy_machine_options(default_opts); - object_set_properties_from_keyval(OBJECT(current_machine), default_opts, - false, &error_abort); + object_set_props_from_keyval(OBJECT(current_machine), default_opts, + false, &error_abort); qobject_unref(default_opts); } }
diff --git a/tests/Makefile.include b/tests/Makefile.include index f257288..a063a1d 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include
@@ -14,7 +14,8 @@ @echo " $(MAKE) check-unit Run qobject tests" @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" @echo " $(MAKE) check-tracetool Run tracetool generator tests" - @echo " $(MAKE) check-block Run block tests" + @echo " $(MAKE) check-block Run block tests (all formats/protocols)" + @echo " $(MAKE) check-block-DRIVER Run block tests (only for format/protocol 'DRIVER')" ifneq ($(filter $(all-check-targets), check-softfloat),) @echo " $(MAKE) check-softfloat Run FPU emulation tests" @echo " $(MAKE) check-tcg Run TCG tests"
diff --git a/tests/qemu-iotests/128 b/tests/qemu-iotests/128 index d0e00d2..d75b1a4 100755 --- a/tests/qemu-iotests/128 +++ b/tests/qemu-iotests/128
@@ -42,6 +42,12 @@ echo "0 $((1024 * 1024 * 1024 / 512)) error" | \ $cmd dmsetup create "$devname" 2>/dev/null if [ "$?" -eq 0 ]; then + DEV="/dev/mapper/$devname" + if ! -e $DEV + then + _notrun "Device $DEV not appearing" + fi + sudo="$cmd" return fi
diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index c13343d..6dff39a 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149
@@ -95,11 +95,14 @@ args = ["sudo", "-n", "/bin/true"] - proc = subprocess.Popen(args, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + try: + proc = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + except FileNotFoundError as e: + iotests.notrun('requires sudo binary: %s' % e) msg = proc.communicate()[0]
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 17489fb..a62ae8d 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185
@@ -50,6 +50,7 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux +_flaky_test https://gitlab.com/qemu-project/qemu/-/issues/3270 size=$((64 * 1048576)) TEST_IMG="${TEST_IMG}.base" _make_test_img $size
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 731e4b2..298bc48 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc
@@ -1088,5 +1088,21 @@ fi } +# This must be referenced after any _require_ lines, so that +# test filtering happens first +_flaky_test() +{ + if test -z "$1" + then + echo "A GitLab issue URL must be provided for a flaky test" + exit 1 + fi + + if test -z "$QEMU_TEST_FLAKY_TESTS" + then + _notrun "Test is flaky (see $1) and \$QEMU_TEST_FLAKY_TESTS is not set" + fi +} + # make sure this script returns success true
diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build index d7bae71..bc6132a 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build
@@ -10,15 +10,19 @@ qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd] qemu_iotests_env = {'PYTHON': python.full_path()} -qemu_iotests_formats = { +# If altering this definition, also update docs/devel/testing/main.rst +# section on 'check-block' targets to reflect the changes +qemu_iotests_drivers = { 'qcow2': 'quick', 'raw': 'slow', + 'luks': 'thorough', + 'nbd': 'thorough', 'parallels': 'thorough', 'qed': 'thorough', 'vdi': 'thorough', 'vhdx': 'thorough', 'vmdk': 'thorough', - 'vpc': 'thorough' + 'vpc': 'thorough', } foreach k, v : emulators @@ -29,32 +33,71 @@ qemu_iotests_check_cmd = files('check') -foreach format, speed: qemu_iotests_formats +foreach driver, speed: qemu_iotests_drivers + # Drivers tagged 'quick' get the subset of tests in the 'auto' + # group, run by default with 'make check' / 'make check-block' + seen = [] if speed == 'quick' - suites = 'block' - else - suites = ['block-' + speed, speed] + args = ['-tap', '-' + driver, '-g', 'auto'] + suites = ['block'] + + rc = run_command( + [python, qemu_iotests_check_cmd] + args + ['-n'], + check: true, + ) + + foreach item: rc.stdout().strip().split() + seen += item + args = [qemu_iotests_check_cmd, + '-tap', '-' + driver, item, + '--source-dir', meson.current_source_dir(), + '--build-dir', meson.current_build_dir()] + # Some individual tests take as long as 45 seconds + # Bump the timeout to 3 minutes for some headroom + # on slow machines to minimize spurious failures + test('io-' + driver + '-' + item, + python, + args: args, + depends: qemu_iotests_binaries, + env: qemu_iotests_env, + protocol: 'tap', + timeout: 180, + suite: suites) + endforeach endif - args = ['-tap', '-' + format] - if speed == 'quick' - args += ['-g', 'auto'] + # Every driver gets put in the driver specific suite + suites = ['block-' + driver + '-optional'] + # Any driver tagged quick or slow also gets added to slow + # otherwise its tagged thorough + if speed != 'thorough' + suites += ['block-slow'] + else + suites += ['block-thorough'] endif + args = ['-tap', '-' + driver] + rc = run_command( [python, qemu_iotests_check_cmd] + args + ['-n'], check: true, ) foreach item: rc.stdout().strip().split() + # Skip any tests already added from the 'auto' group + # as they're run in the 'quick' suite already + if item in seen + continue + endif + args = [qemu_iotests_check_cmd, - '-tap', '-' + format, item, + '-tap', '-' + driver, item, '--source-dir', meson.current_source_dir(), '--build-dir', meson.current_build_dir()] # Some individual tests take as long as 45 seconds # Bump the timeout to 3 minutes for some headroom # on slow machines to minimize spurious failures - test('io-' + format + '-' + item, + test('io-' + driver + '-' + item, python, args: args, depends: qemu_iotests_binaries,
diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index e2a3658..dbe2ddd 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py
@@ -174,7 +174,7 @@ def test_print_one_line(self, test: str, elif status == 'fail': print(f'not ok {self.env.imgfmt} {test}') elif status == 'not run': - print(f'ok {self.env.imgfmt} {test} # SKIP') + print(f'ok {self.env.imgfmt} {test} # SKIP {description}') return if lasttime:
diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index ee3c6fb..89de92b 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c
@@ -336,7 +336,7 @@ }; -static void test_dummy_createv(void) +static void test_dummy_createv_tree(void) { Error *err = NULL; Object *parent = object_get_objects_root(); @@ -351,6 +351,7 @@ NULL)); g_assert(err == NULL); + g_assert_cmpint(dobj->parent_obj.ref, ==, 1); g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); g_assert(dobj->bv == true); g_assert(dobj->av == DUMMY_PLATYPUS); @@ -362,9 +363,30 @@ } -static Object *new_helper(Error **errp, - Object *parent, - ...) +static void test_dummy_createv_parentless(void) +{ + Error *err = NULL; + DummyObject *dobj = DUMMY_OBJECT( + object_new_with_props_parentless(TYPE_DUMMY, + &err, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "platypus", + NULL)); + + g_assert(err == NULL); + g_assert_cmpint(dobj->parent_obj.ref, ==, 1); + g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); + g_assert(dobj->bv == true); + g_assert(dobj->av == DUMMY_PLATYPUS); + + object_unref(OBJECT(dobj)); +} + + +static Object *new_helper_tree(Error **errp, + Object *parent, + ...) { va_list vargs; Object *obj; @@ -373,25 +395,26 @@ obj = object_new_with_propv(TYPE_DUMMY, parent, "dummy0", - errp, - vargs); + vargs, + errp); va_end(vargs); return obj; } -static void test_dummy_createlist(void) +static void test_dummy_createlist_tree(void) { Error *err = NULL; Object *parent = object_get_objects_root(); DummyObject *dobj = DUMMY_OBJECT( - new_helper(&err, - parent, - "bv", "yes", - "sv", "Hiss hiss hiss", - "av", "platypus", - NULL)); + new_helper_tree(&err, + parent, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "platypus", + NULL)); g_assert(err == NULL); + g_assert_cmpint(dobj->parent_obj.ref, ==, 1); g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); g_assert(dobj->bv == true); g_assert(dobj->av == DUMMY_PLATYPUS); @@ -402,13 +425,45 @@ object_unparent(OBJECT(dobj)); } +static Object *new_helper_parentless(Error **errp, + ...) +{ + va_list vargs; + Object *obj; + + va_start(vargs, errp); + obj = object_new_with_propv_parentless(TYPE_DUMMY, + vargs, + errp); + va_end(vargs); + return obj; +} + +static void test_dummy_createlist_parentless(void) +{ + Error *err = NULL; + DummyObject *dobj = DUMMY_OBJECT( + new_helper_parentless(&err, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "platypus", + NULL)); + + g_assert(err == NULL); + g_assert_cmpint(dobj->parent_obj.ref, ==, 1); + g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); + g_assert(dobj->bv == true); + g_assert(dobj->av == DUMMY_PLATYPUS); + + object_unref(OBJECT(dobj)); +} + static bool test_create_obj(QDict *qdict, Error **errp) { Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - Object *obj = user_creatable_add_type(TYPE_DUMMY, "dev0", qdict, v, errp); - + Object *obj = object_new_with_props_from_qdict( + TYPE_DUMMY, object_get_objects_root(), "dev0", qdict, v, errp); visit_free(v); - object_unref(obj); return !!obj; } @@ -658,8 +713,14 @@ type_register_static(&dummy_bus_info); type_register_static(&dummy_backend_info); - g_test_add_func("/qom/proplist/createlist", test_dummy_createlist); - g_test_add_func("/qom/proplist/createv", test_dummy_createv); + g_test_add_func("/qom/proplist/createlist/tree", + test_dummy_createlist_tree); + g_test_add_func("/qom/proplist/createlist/parentless", + test_dummy_createlist_parentless); + g_test_add_func("/qom/proplist/createv/tree", + test_dummy_createv_tree); + g_test_add_func("/qom/proplist/createv/parentless", + test_dummy_createv_parentless); g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl); g_test_add_func("/qom/proplist/badenum", test_dummy_badenum); g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index 0d06a68..dc7a01b 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c
@@ -24,6 +24,7 @@ #include "crypto-tls-psk-helpers.h" #include "crypto/tlscredsx509.h" #include "crypto/tlscredspsk.h" +#include "crypto/tlscredsanon.h" #include "crypto/tlssession.h" #include "qom/object_interfaces.h" #include "qapi/error.h" @@ -190,6 +191,121 @@ } +static QCryptoTLSCreds *test_tls_creds_anon_create( + QCryptoTLSCredsEndpoint endpoint) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_ANON, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &error_abort, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "priority", "NORMAL", + NULL + ); + return QCRYPTO_TLS_CREDS(creds); +} + + +static void test_crypto_tls_session_anon(void) +{ + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + int ret; + + /* We'll use this for our fake client-server connection */ + ret = qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + g_assert(ret == 0); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qemu_set_blocking(channel[0], false, &error_abort); + qemu_set_blocking(channel[1], false, &error_abort); + + clientCreds = test_tls_creds_anon_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_anon_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER); + g_assert(serverCreds != NULL); + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); + g_assert(serverSess != NULL); + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + qcrypto_tls_session_set_callbacks(serverSess, + testWrite, testRead, + &channel[0]); + qcrypto_tls_session_set_callbacks(clientSess, + testWrite, testRead, + &channel[1]); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + do { + int rv; + if (!serverShake) { + rv = qcrypto_tls_session_handshake(serverSess, + &error_abort); + g_assert(rv >= 0); + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + serverShake = true; + } + } + if (!clientShake) { + rv = qcrypto_tls_session_handshake(clientSess, + &error_abort); + g_assert(rv >= 0); + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + clientShake = true; + } + } + } while (!clientShake || !serverShake); + + + /* Finally make sure the server & client validation is successful. */ + g_assert(qcrypto_tls_session_check_credentials(serverSess, + &error_abort) == 0); + g_assert(qcrypto_tls_session_check_credentials(clientSess, + &error_abort) == 0); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + struct QCryptoTLSSessionTestData { const char *servercacrt; const char *clientcacrt; @@ -421,9 +537,11 @@ test_tls_init(KEYFILE); test_tls_psk_init(PSKFILE); - /* Simple initial test using Pre-Shared Keys. */ + /* Simple initial tests using Pre-Shared Keys & anon creds */ g_test_add_func("/qcrypto/tlssession/psk", test_crypto_tls_session_psk); + g_test_add_func("/qcrypto/tlssession/anon", + test_crypto_tls_session_anon); /* More complex tests using X.509 certificates. */ # define TEST_SESS_REG(name, caCrt, \