Add a qtest that verifies display consoles are dynamically added and
removed over D-Bus when a bochs-display device is hotplugged and
unplugged on a q35 machine.

The test plugs device_add a bochs-display, waits for the DEVICE_ADDED
QMP event, and checks that the D-Bus VM interface reports a second
console. It then device_del it, forces a system reset
(q35 removal is ACPI-based and needs guest cooperation qtest cannot
provide), waits for DEVICE_DELETED, and checks the console count again.

Signed-off-by: Marc-André Lureau <[email protected]>
---
 tests/qtest/dbus-display-test.c | 101 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 99 insertions(+), 2 deletions(-)

diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c
index 5773776cad5..7838ce7323f 100644
--- a/tests/qtest/dbus-display-test.c
+++ b/tests/qtest/dbus-display-test.c
@@ -7,6 +7,8 @@
 #include <gio/gio.h>
 #include <gio/gunixfdlist.h>
 #include "libqtest.h"
+#include "qobject/qdict.h"
+#include "qobject/qstring.h"
 #include "ui/dbus-display1.h"
 
 static GDBusConnection*
@@ -38,11 +40,11 @@ test_dbus_p2p_from_fd(int fd)
 }
 
 static void
-test_setup(QTestState **qts, GDBusConnection **conn)
+test_setup_args(QTestState **qts, GDBusConnection **conn, const char *args)
 {
     int pair[2];
 
-    *qts = qtest_init("-display dbus,p2p=yes -name dbus-test");
+    *qts = qtest_init(args);
 
     g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
 
@@ -52,6 +54,12 @@ test_setup(QTestState **qts, GDBusConnection **conn)
     g_dbus_connection_start_message_processing(*conn);
 }
 
+static void
+test_setup(QTestState **qts, GDBusConnection **conn)
+{
+    test_setup_args(qts, conn, "-display dbus,p2p=yes -name dbus-test");
+}
+
 static void
 test_dbus_display_vm(void)
 {
@@ -360,6 +368,92 @@ test_dbus_display_keyboard(void)
     qtest_quit(qts);
 }
 
+static gsize
+get_console_ids_count(GDBusConnection *conn)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(QemuDBusDisplay1VMProxy) vm = NULL;
+    GVariant *console_ids;
+    gsize n_ids = 0;
+
+    vm = QEMU_DBUS_DISPLAY1_VM_PROXY(
+        qemu_dbus_display1_vm_proxy_new_sync(
+            conn,
+            G_DBUS_PROXY_FLAGS_NONE,
+            NULL,
+            DBUS_DISPLAY1_ROOT "/VM",
+            NULL,
+            &err));
+    g_assert_no_error(err);
+
+    console_ids = qemu_dbus_display1_vm_get_console_ids(
+        QEMU_DBUS_DISPLAY1_VM(vm));
+    if (console_ids) {
+        n_ids = g_variant_n_children(console_ids);
+    }
+    return n_ids;
+}
+
+static void
+wait_device_event(QTestState *qts, const char *event_name, const char *id)
+{
+    QDict *resp, *data;
+    QString *qstr;
+
+    for (;;) {
+        resp = qtest_qmp_eventwait_ref(qts, event_name);
+        data = qdict_get_qdict(resp, "data");
+        if (!data || !qdict_get(data, "device")) {
+            qobject_unref(resp);
+            continue;
+        }
+        qstr = qobject_to(QString, qdict_get(data, "device"));
+        if (!strcmp(qstring_get_str(qstr), id)) {
+            qobject_unref(resp);
+            break;
+        }
+        qobject_unref(resp);
+    }
+}
+
+static void
+test_dbus_display_hotplug(void)
+{
+    g_autoptr(GDBusConnection) conn = NULL;
+    QTestState *qts = NULL;
+    gsize n;
+
+    test_setup_args(&qts, &conn,
+        "-machine q35"
+        " -device pcie-root-port,id=rp0"
+        " -display dbus,p2p=yes"
+        " -name dbus-test");
+
+    n = get_console_ids_count(conn);
+    g_assert_cmpuint(n, ==, 1);
+
+    qtest_qmp_device_add(qts, "bochs-display", "bochs0",
+                         "{'bus': 'rp0'}");
+
+    n = get_console_ids_count(conn);
+    g_assert_cmpuint(n, ==, 2);
+
+    /*
+     * On q35, PCI device removal is ACPI-based and requires guest
+     * acknowledgement. Since qtest has no guest OS, issue the delete
+     * request and force removal via system reset.
+     */
+    qtest_qmp_device_del_send(qts, "bochs0");
+    qtest_system_reset_nowait(qts);
+    wait_device_event(qts, "DEVICE_DELETED", "bochs0");
+
+    n = get_console_ids_count(conn);
+    g_assert_cmpuint(n, ==, 1);
+
+    g_clear_object(&conn);
+    qtest_quit(qts);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -369,6 +463,9 @@ main(int argc, char **argv)
     qtest_add_data_func("/dbus-display/console", GINT_TO_POINTER(false), 
test_dbus_display_console);
     qtest_add_data_func("/dbus-display/console/map", GINT_TO_POINTER(true), 
test_dbus_display_console);
     qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard);
+    if (qtest_has_machine("q35") && qtest_has_device("bochs-display")) {
+        qtest_add_func("/dbus-display/hotplug", test_dbus_display_hotplug);
+    }
 
     return g_test_run();
 }

-- 
2.54.0


Reply via email to