Commit f741225073 for qemu.org
commit f74122507380ee53170c5b62e733bc247a76b4be
Author: Marc-André Lureau <marcandre.lureau@redhat.com>
Date: Fri Apr 24 20:28:11 2026 +0400
qtest: add "qom-tests" command
Add a new "qom-tests" to exercise basic object lifecycle. Instantiate
all non-abstract objects, get and set properties and unref.
This should quickly find leaks and other related issues that are
eventually triggerable at run-time with QMP qom commands.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
diff --git a/system/qtest.c b/system/qtest.c
index d6db057b0a..fd37bcbfaa 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -31,6 +31,8 @@
#include "qemu/cutils.h"
#include "qemu/target-info.h"
#include "qom/object_interfaces.h"
+#include "qom/qom-qobject.h"
+#include "qobject/qobject.h"
#define MAX_IRQ 256
@@ -754,6 +756,50 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
new_ns = qemu_clock_advance_virtual_time(ns);
qtest_sendf(chr, "%s %"PRIi64"\n",
new_ns == ns ? "OK" : "FAIL", new_ns);
+ } else if (strcmp(words[0], "qom-tests") == 0) {
+ GSList *list, *l;
+
+ list = object_class_get_list(NULL, false);
+ for (l = list; l; l = l->next) {
+ ObjectClass *klass = l->data;
+ const char *type_name = object_class_get_name(klass);
+ Object *obj;
+ ObjectPropertyIterator iter;
+ ObjectProperty *prop;
+
+ obj = object_new_with_class(klass);
+ object_property_iter_init(&iter, obj);
+ while ((prop = object_property_iter_next(&iter))) {
+ QObject *value;
+ Error *local_err = NULL;
+
+ value = object_property_get_qobject(obj, prop->name,
+ &local_err);
+ if (local_err) {
+ error_report("qom-tests: %s.%s: get failed: %s",
+ type_name, prop->name,
+ error_get_pretty(local_err));
+ error_free(local_err);
+ continue;
+ }
+
+ if (prop->set) {
+ if (!object_property_set_qobject(obj, prop->name, value,
+ &local_err)) {
+ error_report("qom-tests: %s.%s: set failed: %s",
+ type_name, prop->name,
+ error_get_pretty(local_err));
+ error_free(local_err);
+ }
+ }
+
+ qobject_unref(value);
+ }
+
+ object_unref(obj);
+ }
+ g_slist_free(list);
+ qtest_send(chr, "OK\n");
} else if (process_command_cb && process_command_cb(chr, words)) {
/* Command got consumed by the callback handler */
} else {
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index b1e06ea364..4e22c66b75 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -1137,6 +1137,12 @@ void qtest_module_load(QTestState *s, const char *prefix, const char *libname)
qtest_rsp(s);
}
+void qtest_qom_tests(QTestState *s)
+{
+ qtest_sendf(s, "qom-tests\n");
+ qtest_rsp(s);
+}
+
static int64_t qtest_clock_rsp(QTestState *s)
{
gchar **words;
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 950ea2baaf..45217fb8dc 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -426,6 +426,14 @@ char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap)
void qtest_module_load(QTestState *s, const char *prefix, const char *libname);
+/**
+ * qtest_qom_tests:
+ * @s: #QTestState instance to operate on.
+ *
+ * Run QOM property get/set round-trip tests on all non-abstract types.
+ */
+void qtest_qom_tests(QTestState *s);
+
/**
* qtest_get_irq:
* @s: #QTestState instance to operate on.
diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c
index 6421f2d9d9..cf4c6b5add 100644
--- a/tests/qtest/qom-test.c
+++ b/tests/qtest/qom-test.c
@@ -227,6 +227,17 @@ static void add_machine_test_case(const char *mname)
g_free(path);
}
+static void test_qom_qtests(void)
+{
+ QTestState *qts;
+
+ qts = qtest_initf("-machine none");
+
+ qtest_qom_tests(qts);
+
+ qtest_quit(qts);
+}
+
int main(int argc, char **argv)
{
char *v_env = getenv("V");
@@ -238,6 +249,7 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_cb_for_every_machine(add_machine_test_case, g_test_quick());
+ qtest_add_func("qom/qom-qtests", test_qom_qtests);
return g_test_run();
}