diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c new file mode 100644 index 0000000000..43c77aff04 --- /dev/null +++ b/tests/qtest/dbus-display-test.c @@ -0,0 +1,257 @@ +#include "qemu/osdep.h" +#include "qemu/dbus.h" +#include +#include +#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(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 913e987409..1b2bde6660 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -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')