1 #include "qemu/osdep.h"
2 #include "qemu/dbus.h"
3 #include <gio/gio.h>
4 #include <gio/gunixfdlist.h>
5 #include "libqtest.h"
6 #include "ui/dbus-display1.h"
7 
8 static GDBusConnection*
9 test_dbus_p2p_from_fd(int fd)
10 {
11     g_autoptr(GError) err = NULL;
12     g_autoptr(GSocket) socket = NULL;
13     g_autoptr(GSocketConnection) socketc = NULL;
14     GDBusConnection *conn;
15 
16     socket = g_socket_new_from_fd(fd, &err);
17     g_assert_no_error(err);
18 
19     socketc = g_socket_connection_factory_create_connection(socket);
20     g_assert(socketc != NULL);
21 
22     conn = g_dbus_connection_new_sync(
23         G_IO_STREAM(socketc), NULL,
24         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
25         G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
26         NULL, NULL, &err);
27     g_assert_no_error(err);
28 
29     return conn;
30 }
31 
32 static void
33 test_setup(QTestState **qts, GDBusConnection **conn)
34 {
35     int pair[2];
36 
37     *qts = qtest_init("-display dbus,p2p=yes -name dbus-test");
38 
39     g_assert_cmpint(socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
40 
41     qtest_qmp_add_client(*qts, "@dbus-display", pair[1]);
42 
43     *conn = test_dbus_p2p_from_fd(pair[0]);
44     g_dbus_connection_start_message_processing(*conn);
45 }
46 
47 static void
48 test_dbus_display_vm(void)
49 {
50     g_autoptr(GError) err = NULL;
51     g_autoptr(GDBusConnection) conn = NULL;
52     g_autoptr(QemuDBusDisplay1VMProxy) vm = NULL;
53     QTestState *qts = NULL;
54 
55     test_setup(&qts, &conn);
56 
57     vm = QEMU_DBUS_DISPLAY1_VM_PROXY(
58         qemu_dbus_display1_vm_proxy_new_sync(
59             conn,
60             G_DBUS_PROXY_FLAGS_NONE,
61             NULL,
62             DBUS_DISPLAY1_ROOT "/VM",
63             NULL,
64             &err));
65     g_assert_no_error(err);
66 
67     g_assert_cmpstr(
68         qemu_dbus_display1_vm_get_name(QEMU_DBUS_DISPLAY1_VM(vm)),
69         ==,
70         "dbus-test");
71     qtest_quit(qts);
72 }
73 
74 typedef struct TestDBusConsoleRegister {
75     GMainLoop *loop;
76     GThread *thread;
77     GDBusConnection *listener_conn;
78     GDBusObjectManagerServer *server;
79 } TestDBusConsoleRegister;
80 
81 static gboolean listener_handle_scanout(
82     QemuDBusDisplay1Listener *object,
83     GDBusMethodInvocation *invocation,
84     guint arg_width,
85     guint arg_height,
86     guint arg_stride,
87     guint arg_pixman_format,
88     GVariant *arg_data,
89     TestDBusConsoleRegister *test)
90 {
91     g_main_loop_quit(test->loop);
92 
93     return DBUS_METHOD_INVOCATION_HANDLED;
94 }
95 
96 static void
97 test_dbus_console_setup_listener(TestDBusConsoleRegister *test)
98 {
99     g_autoptr(GDBusObjectSkeleton) listener = NULL;
100     g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL;
101 
102     test->server = g_dbus_object_manager_server_new(DBUS_DISPLAY1_ROOT);
103     listener = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/Listener");
104     iface = QEMU_DBUS_DISPLAY1_LISTENER_SKELETON(
105         qemu_dbus_display1_listener_skeleton_new());
106     g_object_connect(iface,
107                      "signal::handle-scanout", listener_handle_scanout, test,
108                      NULL);
109     g_dbus_object_skeleton_add_interface(listener,
110                                          G_DBUS_INTERFACE_SKELETON(iface));
111     g_dbus_object_manager_server_export(test->server, listener);
112     g_dbus_object_manager_server_set_connection(test->server,
113                                                 test->listener_conn);
114 
115     g_dbus_connection_start_message_processing(test->listener_conn);
116 }
117 
118 static void
119 test_dbus_console_registered(GObject *source_object,
120                              GAsyncResult *res,
121                              gpointer user_data)
122 {
123     TestDBusConsoleRegister *test = user_data;
124     g_autoptr(GError) err = NULL;
125 
126     qemu_dbus_display1_console_call_register_listener_finish(
127         QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
128         NULL, res, &err);
129     g_assert_no_error(err);
130 
131     test->listener_conn = g_thread_join(test->thread);
132     test_dbus_console_setup_listener(test);
133 }
134 
135 static gpointer
136 test_dbus_p2p_server_setup_thread(gpointer data)
137 {
138     return test_dbus_p2p_from_fd(GPOINTER_TO_INT(data));
139 }
140 
141 static void
142 test_dbus_display_console(void)
143 {
144     g_autoptr(GError) err = NULL;
145     g_autoptr(GDBusConnection) conn = NULL;
146     g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL;
147     g_autoptr(GUnixFDList) fd_list = NULL;
148     g_autoptr(GMainLoop) loop = NULL;
149     QTestState *qts = NULL;
150     int pair[2], idx;
151     TestDBusConsoleRegister test;
152 
153     test_setup(&qts, &conn);
154 
155     g_assert_cmpint(socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
156     fd_list = g_unix_fd_list_new();
157     idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
158 
159     console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY(
160         qemu_dbus_display1_console_proxy_new_sync(
161             conn,
162             G_DBUS_PROXY_FLAGS_NONE,
163             NULL,
164             "/org/qemu/Display1/Console_0",
165             NULL,
166             &err));
167     g_assert_no_error(err);
168 
169     test.loop = loop = g_main_loop_new(NULL, FALSE);
170     test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread,
171                                GINT_TO_POINTER(pair[0]));
172 
173     qemu_dbus_display1_console_call_register_listener(
174         QEMU_DBUS_DISPLAY1_CONSOLE(console),
175         g_variant_new_handle(idx),
176         G_DBUS_CALL_FLAGS_NONE,
177         -1,
178         fd_list,
179         NULL,
180         test_dbus_console_registered,
181         &test);
182 
183     g_main_loop_run(loop);
184 
185     g_clear_object(&test.server);
186     g_clear_object(&test.listener_conn);
187     qtest_quit(qts);
188 }
189 
190 static void
191 test_dbus_display_keyboard(void)
192 {
193     g_autoptr(GError) err = NULL;
194     g_autoptr(GDBusConnection) conn = NULL;
195     g_autoptr(QemuDBusDisplay1KeyboardProxy) keyboard = NULL;
196     QTestState *qts = NULL;
197 
198     test_setup(&qts, &conn);
199 
200     keyboard = QEMU_DBUS_DISPLAY1_KEYBOARD_PROXY(
201         qemu_dbus_display1_keyboard_proxy_new_sync(
202             conn,
203             G_DBUS_PROXY_FLAGS_NONE,
204             NULL,
205             "/org/qemu/Display1/Console_0",
206             NULL,
207             &err));
208     g_assert_no_error(err);
209 
210 
211     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 0);
212     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0);
213 
214     qemu_dbus_display1_keyboard_call_press_sync(
215         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
216         0x1C, /* qnum enter */
217         G_DBUS_CALL_FLAGS_NONE,
218         -1,
219         NULL,
220         &err);
221     g_assert_no_error(err);
222 
223     /* may be should wait for interrupt? */
224     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
225     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
226 
227     qemu_dbus_display1_keyboard_call_release_sync(
228         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
229         0x1C, /* qnum enter */
230         G_DBUS_CALL_FLAGS_NONE,
231         -1,
232         NULL,
233         &err);
234     g_assert_no_error(err);
235 
236     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
237     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0xF0); /* scan code 2 release */
238     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
239 
240     g_assert_cmpint(qemu_dbus_display1_keyboard_get_modifiers(
241                         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard)), ==, 0);
242 
243     qtest_quit(qts);
244 }
245 
246 int
247 main(int argc, char **argv)
248 {
249     g_test_init(&argc, &argv, NULL);
250 
251     qtest_add_func("/dbus-display/vm", test_dbus_display_vm);
252     qtest_add_func("/dbus-display/console", test_dbus_display_console);
253     qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard);
254 
255     return g_test_run();
256 }
257