Marc-André Lureau <[email protected]> writes:

> 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();
>  }

Reviewed-by: Fabiano Rosas <[email protected]>

Reply via email to