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