tests: start dbus-display-test
authorMarc-André Lureau <marcandre.lureau@redhat.com>
Wed, 6 Oct 2021 21:08:15 +0000 (01:08 +0400)
committerMarc-André Lureau <marcandre.lureau@redhat.com>
Tue, 21 Dec 2021 06:50:22 +0000 (10:50 +0400)
Cover basic display interface usage. More cases to be added to cover
disconnections, multiple connections, corner cases. At this point, they
would be better written in Rust or Python though.

The proxy also covers reading the properties, since they are
automatically loaded at creation.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
tests/qtest/dbus-display-test.c [new file with mode: 0644]
tests/qtest/meson.build

diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c
new file mode 100644 (file)
index 0000000..43c77af
--- /dev/null
@@ -0,0 +1,257 @@
+#include "qemu/osdep.h"
+#include "qemu/dbus.h"
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include "libqos/libqtest.h"
+#include "qemu-common.h"
+#include "dbus-display1.h"
+
+static GDBusConnection*
+test_dbus_p2p_from_fd(int fd)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GSocket) socket = NULL;
+    g_autoptr(GSocketConnection) socketc = NULL;
+    GDBusConnection *conn;
+
+    socket = g_socket_new_from_fd(fd, &err);
+    g_assert_no_error(err);
+
+    socketc = g_socket_connection_factory_create_connection(socket);
+    g_assert(socketc != NULL);
+
+    conn = g_dbus_connection_new_sync(
+        G_IO_STREAM(socketc), NULL,
+        G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+        G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
+        NULL, NULL, &err);
+    g_assert_no_error(err);
+
+    return conn;
+}
+
+static void
+test_setup(QTestState **qts, GDBusConnection **conn)
+{
+    int pair[2];
+
+    *qts = qtest_init("-display dbus,p2p=yes -name dbus-test");
+
+    g_assert_cmpint(socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
+
+    qtest_qmp_add_client(*qts, "@dbus-display", pair[1]);
+
+    *conn = test_dbus_p2p_from_fd(pair[0]);
+    g_dbus_connection_start_message_processing(*conn);
+}
+
+static void
+test_dbus_display_vm(void)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GDBusConnection) conn = NULL;
+    g_autoptr(QemuDBusDisplay1VMProxy) vm = NULL;
+    QTestState *qts = NULL;
+
+    test_setup(&qts, &conn);
+
+    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);
+
+    g_assert_cmpstr(
+        qemu_dbus_display1_vm_get_name(QEMU_DBUS_DISPLAY1_VM(vm)),
+        ==,
+        "dbus-test");
+    qtest_quit(qts);
+}
+
+typedef struct TestDBusConsoleRegister {
+    GMainLoop *loop;
+    GThread *thread;
+    GDBusConnection *listener_conn;
+    GDBusObjectManagerServer *server;
+} TestDBusConsoleRegister;
+
+static gboolean listener_handle_scanout(
+    QemuDBusDisplay1Listener *object,
+    GDBusMethodInvocation *invocation,
+    guint arg_width,
+    guint arg_height,
+    guint arg_stride,
+    guint arg_pixman_format,
+    GVariant *arg_data,
+    TestDBusConsoleRegister *test)
+{
+    g_main_loop_quit(test->loop);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+test_dbus_console_setup_listener(TestDBusConsoleRegister *test)
+{
+    g_autoptr(GDBusObjectSkeleton) listener = NULL;
+    g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL;
+
+    test->server = g_dbus_object_manager_server_new(DBUS_DISPLAY1_ROOT);
+    listener = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/Listener");
+    iface = QEMU_DBUS_DISPLAY1_LISTENER_SKELETON(
+        qemu_dbus_display1_listener_skeleton_new());
+    g_object_connect(iface,
+                     "signal::handle-scanout", listener_handle_scanout, test,
+                     NULL);
+    g_dbus_object_skeleton_add_interface(listener,
+                                         G_DBUS_INTERFACE_SKELETON(iface));
+    g_dbus_object_manager_server_export(test->server, listener);
+    g_dbus_object_manager_server_set_connection(test->server,
+                                                test->listener_conn);
+
+    g_dbus_connection_start_message_processing(test->listener_conn);
+}
+
+static void
+test_dbus_console_registered(GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer user_data)
+{
+    TestDBusConsoleRegister *test = user_data;
+    g_autoptr(GError) err = NULL;
+
+    qemu_dbus_display1_console_call_register_listener_finish(
+        QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
+        NULL, res, &err);
+    g_assert_no_error(err);
+
+    test->listener_conn = g_thread_join(test->thread);
+    test_dbus_console_setup_listener(test);
+}
+
+static gpointer
+test_dbus_p2p_server_setup_thread(gpointer data)
+{
+    return test_dbus_p2p_from_fd(GPOINTER_TO_INT(data));
+}
+
+static void
+test_dbus_display_console(void)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GDBusConnection) conn = NULL;
+    g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL;
+    g_autoptr(GUnixFDList) fd_list = NULL;
+    g_autoptr(GMainLoop) loop = NULL;
+    QTestState *qts = NULL;
+    int pair[2], idx;
+    TestDBusConsoleRegister test;
+
+    test_setup(&qts, &conn);
+
+    g_assert_cmpint(socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
+    fd_list = g_unix_fd_list_new();
+    idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
+
+    console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY(
+        qemu_dbus_display1_console_proxy_new_sync(
+            conn,
+            G_DBUS_PROXY_FLAGS_NONE,
+            NULL,
+            "/org/qemu/Display1/Console_0",
+            NULL,
+            &err));
+    g_assert_no_error(err);
+
+    test.loop = loop = g_main_loop_new(NULL, FALSE);
+    test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread,
+                               GINT_TO_POINTER(pair[0]));
+
+    qemu_dbus_display1_console_call_register_listener(
+        QEMU_DBUS_DISPLAY1_CONSOLE(console),
+        g_variant_new_handle(idx),
+        G_DBUS_CALL_FLAGS_NONE,
+        -1,
+        fd_list,
+        NULL,
+        test_dbus_console_registered,
+        &test);
+
+    g_main_loop_run(loop);
+
+    g_clear_object(&test.server);
+    g_clear_object(&test.listener_conn);
+    qtest_quit(qts);
+}
+
+static void
+test_dbus_display_keyboard(void)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GDBusConnection) conn = NULL;
+    g_autoptr(QemuDBusDisplay1KeyboardProxy) keyboard = NULL;
+    QTestState *qts = NULL;
+
+    test_setup(&qts, &conn);
+
+    keyboard = QEMU_DBUS_DISPLAY1_KEYBOARD_PROXY(
+        qemu_dbus_display1_keyboard_proxy_new_sync(
+            conn,
+            G_DBUS_PROXY_FLAGS_NONE,
+            NULL,
+            "/org/qemu/Display1/Console_0",
+            NULL,
+            &err));
+    g_assert_no_error(err);
+
+
+    g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 0);
+    g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0);
+
+    qemu_dbus_display1_keyboard_call_press_sync(
+        QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
+        0x1C, /* qnum enter */
+        G_DBUS_CALL_FLAGS_NONE,
+        -1,
+        NULL,
+        &err);
+    g_assert_no_error(err);
+
+    /* may be should wait for interrupt? */
+    g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
+    g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
+
+    qemu_dbus_display1_keyboard_call_release_sync(
+        QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
+        0x1C, /* qnum enter */
+        G_DBUS_CALL_FLAGS_NONE,
+        -1,
+        NULL,
+        &err);
+    g_assert_no_error(err);
+
+    g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
+    g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0xF0); /* scan code 2 release */
+    g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
+
+    g_assert_cmpint(qemu_dbus_display1_keyboard_get_modifiers(
+                        QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard)), ==, 0);
+
+    qtest_quit(qts);
+}
+
+int
+main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    qtest_add_func("/dbus-display/vm", test_dbus_display_vm);
+    qtest_add_func("/dbus-display/console", test_dbus_display_console);
+    qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard);
+
+    return g_test_run();
+}
index 913e987409d56f2ae1352d765828d25c3e8c0acd..1b2bde6660379dd9e8fef70ec72e5341b9aacd5f 100644 (file)
@@ -92,6 +92,10 @@ qtests_i386 = \
    'test-x86-cpuid-compat',
    'numa-test']
 
+if dbus_display
+  qtests_i386 += ['dbus-display-test']
+endif
+
 dbus_daemon = find_program('dbus-daemon', required: false)
 if dbus_daemon.found() and config_host.has_key('GDBUS_CODEGEN')
   # Temporarily disabled due to Patchew failures:
@@ -265,6 +269,10 @@ qtests = {
   'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
 }
 
+if dbus_display
+qtests += {'dbus-display-test': [dbus_display1, gio]}
+endif
+
 qtest_executables = {}
 foreach dir : target_dirs
   if not dir.endswith('-softmmu')