Skip to content

Commit

Permalink
tests: start dbus-display-test
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
elmarco committed Dec 21, 2021
1 parent 2c7294d commit b4dd5b6
Show file tree
Hide file tree
Showing 2 changed files with 265 additions and 0 deletions.
257 changes: 257 additions & 0 deletions tests/qtest/dbus-display-test.c
Original file line number Diff line number Diff line change
@@ -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();
}
8 changes: 8 additions & 0 deletions tests/qtest/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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')
Expand Down

0 comments on commit b4dd5b6

Please sign in to comment.