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>
Marc-André Lureau committed
Apr 24, 2026 at 20:28 UTC
f74122507380ee53170c5b62e733bc247a76b4be
4 files changed
+72
system/qtest.c
+46
@@ -31,6 +31,8 @@
31
#include "qemu/cutils.h"
32
#include "qemu/target-info.h"
33
#include "qom/object_interfaces.h"
34
+#include "qom/qom-qobject.h"
35
+#include "qobject/qobject.h"
36
37
#define MAX_IRQ 256
38
@@ -754,6 +756,50 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
756
new_ns = qemu_clock_advance_virtual_time(ns);
757
qtest_sendf(chr, "%s %"PRIi64"\n",
758
new_ns == ns ? "OK" : "FAIL", new_ns);
759
+ } else if (strcmp(words[0], "qom-tests") == 0) {
760
+ GSList *list, *l;
761
+
762
+ list = object_class_get_list(NULL, false);
763
+ for (l = list; l; l = l->next) {
764
+ ObjectClass *klass = l->data;
765
+ const char *type_name = object_class_get_name(klass);
766
+ Object *obj;
767
+ ObjectPropertyIterator iter;
768
+ ObjectProperty *prop;
769
+
770
+ obj = object_new_with_class(klass);
771
+ object_property_iter_init(&iter, obj);
772
+ while ((prop = object_property_iter_next(&iter))) {
773
+ QObject *value;
774
+ Error *local_err = NULL;
775
+
776
+ value = object_property_get_qobject(obj, prop->name,
777
+ &local_err);
778
+ if (local_err) {
779
+ error_report("qom-tests: %s.%s: get failed: %s",
780
+ type_name, prop->name,
781
+ error_get_pretty(local_err));
782
+ error_free(local_err);
783
+ continue;
784
+ }
785
+
786
+ if (prop->set) {
787
+ if (!object_property_set_qobject(obj, prop->name, value,
788
+ &local_err)) {
789
+ error_report("qom-tests: %s.%s: set failed: %s",
790
+ type_name, prop->name,
791
+ error_get_pretty(local_err));
792
+ error_free(local_err);
793
+ }
794
+ }
795
+
796
+ qobject_unref(value);
797
+ }
798
+
799
+ object_unref(obj);
800
+ }
801
+ g_slist_free(list);
802
+ qtest_send(chr, "OK\n");
803
} else if (process_command_cb && process_command_cb(chr, words)) {
804
/* Command got consumed by the callback handler */
805
} else {
tests/qtest/libqtest.c
+6
@@ -1137,6 +1137,12 @@ void qtest_module_load(QTestState *s, const char *prefix, const char *libname)
1137
qtest_rsp(s);
1138
}
1139
1140
+void qtest_qom_tests(QTestState *s)
1141
+{
1142
+ qtest_sendf(s, "qom-tests\n");
1143
+ qtest_rsp(s);
1144
+}
1145
+
1146
static int64_t qtest_clock_rsp(QTestState *s)
1147
{
1148
gchar **words;
tests/qtest/libqtest.h
+8
@@ -426,6 +426,14 @@ char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap)
426
427
void qtest_module_load(QTestState *s, const char *prefix, const char *libname);
428
429
+/**
430
+ * qtest_qom_tests:
431
+ * @s: #QTestState instance to operate on.
432
+ *
433
+ * Run QOM property get/set round-trip tests on all non-abstract types.
434
+ */
435
+void qtest_qom_tests(QTestState *s);
436
+
437
/**
438
* qtest_get_irq:
439
* @s: #QTestState instance to operate on.
tests/qtest/qom-test.c
+12
@@ -227,6 +227,17 @@ static void add_machine_test_case(const char *mname)
227
g_free(path);
228
}
229
230
+static void test_qom_qtests(void)
231
+{
232
+ QTestState *qts;
233
+
234
+ qts = qtest_initf("-machine none");
235
+
236
+ qtest_qom_tests(qts);
237
+
238
+ qtest_quit(qts);
239
+}
240
+
241
int main(int argc, char **argv)
242
{
243
char *v_env = getenv("V");
@@ -238,6 +249,7 @@ int main(int argc, char **argv)
249
g_test_init(&argc, &argv, NULL);
250
251
qtest_cb_for_every_machine(add_machine_test_case, g_test_quick());
252
+ qtest_add_func("qom/qom-qtests", test_qom_qtests);
253
254
return g_test_run();
255
}