@samitouri / QOSamiQemu / commits / f741225073

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 }