]>
Commit | Line | Data |
---|---|---|
586ca6ba MAL |
1 | #include "qemu/osdep.h" |
2 | #include <glib/gstdio.h> | |
3 | #include <gio/gio.h> | |
907b5105 | 4 | #include "libqtest.h" |
586ca6ba MAL |
5 | #include "dbus-vmstate1.h" |
6 | #include "migration-helpers.h" | |
7 | ||
8 | static char *workdir; | |
9 | ||
10 | typedef struct TestServerId { | |
11 | const char *name; | |
12 | const char *data; | |
13 | size_t size; | |
14 | } TestServerId; | |
15 | ||
16 | static const TestServerId idA = { | |
17 | "idA", "I'am\0idA!", sizeof("I'am\0idA!") | |
18 | }; | |
19 | ||
20 | static const TestServerId idB = { | |
21 | "idB", "I'am\0idB!", sizeof("I'am\0idB!") | |
22 | }; | |
23 | ||
24 | typedef struct TestServer { | |
25 | const TestServerId *id; | |
26 | bool save_called; | |
27 | bool load_called; | |
28 | } TestServer; | |
29 | ||
30 | typedef struct Test { | |
31 | const char *id_list; | |
32 | bool migrate_fail; | |
33 | bool without_dst_b; | |
34 | TestServer srcA; | |
35 | TestServer dstA; | |
36 | TestServer srcB; | |
37 | TestServer dstB; | |
38 | GMainLoop *loop; | |
39 | QTestState *src_qemu; | |
40 | } Test; | |
41 | ||
42 | static gboolean | |
43 | vmstate_load(VMState1 *object, GDBusMethodInvocation *invocation, | |
44 | const gchar *arg_data, gpointer user_data) | |
45 | { | |
46 | TestServer *h = user_data; | |
47 | g_autoptr(GVariant) var = NULL; | |
48 | GVariant *args; | |
49 | const uint8_t *data; | |
50 | size_t size; | |
51 | ||
52 | args = g_dbus_method_invocation_get_parameters(invocation); | |
53 | var = g_variant_get_child_value(args, 0); | |
54 | data = g_variant_get_fixed_array(var, &size, sizeof(char)); | |
55 | g_assert_cmpuint(size, ==, h->id->size); | |
56 | g_assert(!memcmp(data, h->id->data, h->id->size)); | |
57 | h->load_called = true; | |
58 | ||
59 | g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); | |
60 | return TRUE; | |
61 | } | |
62 | ||
63 | static gboolean | |
64 | vmstate_save(VMState1 *object, GDBusMethodInvocation *invocation, | |
65 | gpointer user_data) | |
66 | { | |
67 | TestServer *h = user_data; | |
68 | GVariant *var; | |
69 | ||
70 | var = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, | |
71 | h->id->data, h->id->size, sizeof(char)); | |
72 | g_dbus_method_invocation_return_value(invocation, | |
73 | g_variant_new("(@ay)", var)); | |
74 | h->save_called = true; | |
75 | ||
76 | return TRUE; | |
77 | } | |
78 | ||
79 | typedef struct WaitNamed { | |
80 | GMainLoop *loop; | |
81 | bool named; | |
82 | } WaitNamed; | |
83 | ||
84 | static void | |
85 | named_cb(GDBusConnection *connection, | |
86 | const gchar *name, | |
87 | gpointer user_data) | |
88 | { | |
89 | WaitNamed *t = user_data; | |
90 | ||
91 | t->named = true; | |
92 | g_main_loop_quit(t->loop); | |
93 | } | |
94 | ||
95 | static GDBusConnection * | |
96 | get_connection(Test *test, guint *ownid) | |
97 | { | |
98 | g_autofree gchar *addr = NULL; | |
99 | WaitNamed *wait; | |
100 | GError *err = NULL; | |
101 | GDBusConnection *c; | |
102 | ||
103 | wait = g_new0(WaitNamed, 1); | |
104 | wait->loop = test->loop; | |
105 | addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &err); | |
106 | g_assert_no_error(err); | |
107 | ||
108 | c = g_dbus_connection_new_for_address_sync( | |
109 | addr, | |
110 | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | | |
111 | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, | |
112 | NULL, NULL, &err); | |
113 | g_assert_no_error(err); | |
114 | *ownid = g_bus_own_name_on_connection(c, "org.qemu.VMState1", | |
115 | G_BUS_NAME_OWNER_FLAGS_NONE, | |
116 | named_cb, named_cb, wait, g_free); | |
117 | if (!wait->named) { | |
118 | g_main_loop_run(wait->loop); | |
119 | } | |
120 | ||
121 | return c; | |
122 | } | |
123 | ||
124 | static GDBusObjectManagerServer * | |
125 | get_server(GDBusConnection *conn, TestServer *s, const TestServerId *id) | |
126 | { | |
127 | g_autoptr(GDBusObjectSkeleton) sk = NULL; | |
128 | g_autoptr(VMState1Skeleton) v = NULL; | |
129 | GDBusObjectManagerServer *os; | |
130 | ||
131 | s->id = id; | |
132 | os = g_dbus_object_manager_server_new("/org/qemu"); | |
133 | sk = g_dbus_object_skeleton_new("/org/qemu/VMState1"); | |
134 | ||
135 | v = VMSTATE1_SKELETON(vmstate1_skeleton_new()); | |
136 | g_object_set(v, "id", id->name, NULL); | |
137 | ||
138 | g_signal_connect(v, "handle-load", G_CALLBACK(vmstate_load), s); | |
139 | g_signal_connect(v, "handle-save", G_CALLBACK(vmstate_save), s); | |
140 | ||
141 | g_dbus_object_skeleton_add_interface(sk, G_DBUS_INTERFACE_SKELETON(v)); | |
142 | g_dbus_object_manager_server_export(os, sk); | |
143 | g_dbus_object_manager_server_set_connection(os, conn); | |
144 | ||
145 | return os; | |
146 | } | |
147 | ||
148 | static void | |
149 | set_id_list(Test *test, QTestState *s) | |
150 | { | |
151 | if (!test->id_list) { | |
152 | return; | |
153 | } | |
154 | ||
155 | g_assert(!qmp_rsp_is_err(qtest_qmp(s, | |
156 | "{ 'execute': 'qom-set', 'arguments': " | |
157 | "{ 'path': '/objects/dv', 'property': 'id-list', 'value': %s } }", | |
158 | test->id_list))); | |
159 | } | |
160 | ||
161 | static gpointer | |
162 | dbus_vmstate_thread(gpointer data) | |
163 | { | |
164 | GMainLoop *loop = data; | |
165 | ||
166 | g_main_loop_run(loop); | |
167 | ||
168 | return NULL; | |
169 | } | |
170 | ||
171 | static void | |
172 | test_dbus_vmstate(Test *test) | |
173 | { | |
174 | g_autofree char *src_qemu_args = NULL; | |
175 | g_autofree char *dst_qemu_args = NULL; | |
176 | g_autoptr(GTestDBus) srcbus = NULL; | |
177 | g_autoptr(GTestDBus) dstbus = NULL; | |
178 | g_autoptr(GDBusConnection) srcconnA = NULL; | |
179 | g_autoptr(GDBusConnection) srcconnB = NULL; | |
180 | g_autoptr(GDBusConnection) dstconnA = NULL; | |
181 | g_autoptr(GDBusConnection) dstconnB = NULL; | |
182 | g_autoptr(GDBusObjectManagerServer) srcserverA = NULL; | |
183 | g_autoptr(GDBusObjectManagerServer) srcserverB = NULL; | |
184 | g_autoptr(GDBusObjectManagerServer) dstserverA = NULL; | |
185 | g_autoptr(GDBusObjectManagerServer) dstserverB = NULL; | |
186 | g_auto(GStrv) srcaddr = NULL; | |
187 | g_auto(GStrv) dstaddr = NULL; | |
188 | g_autoptr(GThread) thread = NULL; | |
189 | g_autoptr(GMainLoop) loop = NULL; | |
190 | g_autofree char *uri = NULL; | |
191 | QTestState *src_qemu = NULL, *dst_qemu = NULL; | |
192 | guint ownsrcA, ownsrcB, owndstA, owndstB; | |
193 | ||
194 | uri = g_strdup_printf("unix:%s/migsocket", workdir); | |
195 | ||
196 | loop = g_main_loop_new(NULL, FALSE); | |
197 | test->loop = loop; | |
198 | ||
199 | srcbus = g_test_dbus_new(G_TEST_DBUS_NONE); | |
200 | g_test_dbus_up(srcbus); | |
201 | srcconnA = get_connection(test, &ownsrcA); | |
202 | srcserverA = get_server(srcconnA, &test->srcA, &idA); | |
203 | srcconnB = get_connection(test, &ownsrcB); | |
204 | srcserverB = get_server(srcconnB, &test->srcB, &idB); | |
205 | ||
206 | /* remove ,guid=foo part */ | |
207 | srcaddr = g_strsplit(g_test_dbus_get_bus_address(srcbus), ",", 2); | |
208 | src_qemu_args = | |
209 | g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s", srcaddr[0]); | |
210 | ||
211 | dstbus = g_test_dbus_new(G_TEST_DBUS_NONE); | |
212 | g_test_dbus_up(dstbus); | |
213 | dstconnA = get_connection(test, &owndstA); | |
214 | dstserverA = get_server(dstconnA, &test->dstA, &idA); | |
215 | if (!test->without_dst_b) { | |
216 | dstconnB = get_connection(test, &owndstB); | |
217 | dstserverB = get_server(dstconnB, &test->dstB, &idB); | |
218 | } | |
219 | ||
220 | dstaddr = g_strsplit(g_test_dbus_get_bus_address(dstbus), ",", 2); | |
221 | dst_qemu_args = | |
222 | g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming %s", | |
223 | dstaddr[0], uri); | |
224 | ||
225 | src_qemu = qtest_init(src_qemu_args); | |
226 | dst_qemu = qtest_init(dst_qemu_args); | |
227 | set_id_list(test, src_qemu); | |
228 | set_id_list(test, dst_qemu); | |
229 | ||
230 | thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop); | |
231 | ||
232 | migrate_qmp(src_qemu, uri, "{}"); | |
233 | test->src_qemu = src_qemu; | |
234 | if (test->migrate_fail) { | |
235 | wait_for_migration_fail(src_qemu, true); | |
1b0f1b14 | 236 | qtest_set_expected_status(dst_qemu, EXIT_FAILURE); |
586ca6ba MAL |
237 | } else { |
238 | wait_for_migration_complete(src_qemu); | |
239 | } | |
240 | ||
241 | qtest_quit(dst_qemu); | |
242 | qtest_quit(src_qemu); | |
243 | g_bus_unown_name(ownsrcA); | |
244 | g_bus_unown_name(ownsrcB); | |
245 | g_bus_unown_name(owndstA); | |
246 | if (!test->without_dst_b) { | |
247 | g_bus_unown_name(owndstB); | |
248 | } | |
249 | ||
250 | g_main_loop_quit(test->loop); | |
251 | } | |
252 | ||
253 | static void | |
254 | check_not_migrated(TestServer *s, TestServer *d) | |
255 | { | |
256 | assert(!s->save_called); | |
257 | assert(!s->load_called); | |
258 | assert(!d->save_called); | |
259 | assert(!d->load_called); | |
260 | } | |
261 | ||
262 | static void | |
263 | check_migrated(TestServer *s, TestServer *d) | |
264 | { | |
265 | assert(s->save_called); | |
266 | assert(!s->load_called); | |
267 | assert(!d->save_called); | |
268 | assert(d->load_called); | |
269 | } | |
270 | ||
271 | static void | |
272 | test_dbus_vmstate_without_list(void) | |
273 | { | |
274 | Test test = { 0, }; | |
275 | ||
276 | test_dbus_vmstate(&test); | |
277 | ||
278 | check_migrated(&test.srcA, &test.dstA); | |
279 | check_migrated(&test.srcB, &test.dstB); | |
280 | } | |
281 | ||
282 | static void | |
283 | test_dbus_vmstate_with_list(void) | |
284 | { | |
285 | Test test = { .id_list = "idA,idB" }; | |
286 | ||
287 | test_dbus_vmstate(&test); | |
288 | ||
289 | check_migrated(&test.srcA, &test.dstA); | |
290 | check_migrated(&test.srcB, &test.dstB); | |
291 | } | |
292 | ||
293 | static void | |
294 | test_dbus_vmstate_only_a(void) | |
295 | { | |
296 | Test test = { .id_list = "idA" }; | |
297 | ||
298 | test_dbus_vmstate(&test); | |
299 | ||
300 | check_migrated(&test.srcA, &test.dstA); | |
301 | check_not_migrated(&test.srcB, &test.dstB); | |
302 | } | |
303 | ||
304 | static void | |
305 | test_dbus_vmstate_missing_src(void) | |
306 | { | |
307 | Test test = { .id_list = "idA,idC", .migrate_fail = true }; | |
308 | ||
309 | /* run in subprocess to silence QEMU error reporting */ | |
310 | if (g_test_subprocess()) { | |
311 | test_dbus_vmstate(&test); | |
312 | check_not_migrated(&test.srcA, &test.dstA); | |
313 | check_not_migrated(&test.srcB, &test.dstB); | |
314 | return; | |
315 | } | |
316 | ||
317 | g_test_trap_subprocess(NULL, 0, 0); | |
318 | g_test_trap_assert_passed(); | |
319 | } | |
320 | ||
321 | static void | |
322 | test_dbus_vmstate_missing_dst(void) | |
323 | { | |
324 | Test test = { .id_list = "idA,idB", | |
325 | .without_dst_b = true, | |
326 | .migrate_fail = true }; | |
327 | ||
328 | /* run in subprocess to silence QEMU error reporting */ | |
329 | if (g_test_subprocess()) { | |
330 | test_dbus_vmstate(&test); | |
331 | assert(test.srcA.save_called); | |
332 | assert(test.srcB.save_called); | |
333 | assert(!test.dstB.save_called); | |
334 | return; | |
335 | } | |
336 | ||
337 | g_test_trap_subprocess(NULL, 0, 0); | |
338 | g_test_trap_assert_passed(); | |
339 | } | |
340 | ||
341 | int | |
342 | main(int argc, char **argv) | |
343 | { | |
344 | GError *err = NULL; | |
345 | g_autofree char *dbus_daemon = NULL; | |
346 | int ret; | |
347 | ||
348 | dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR), | |
349 | "tests", | |
350 | "dbus-vmstate-daemon.sh", | |
351 | NULL); | |
352 | g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true); | |
353 | ||
354 | g_test_init(&argc, &argv, NULL); | |
355 | ||
356 | workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err); | |
357 | if (!workdir) { | |
358 | g_error("Unable to create temporary dir: %s\n", err->message); | |
359 | exit(1); | |
360 | } | |
361 | ||
362 | g_setenv("DBUS_VMSTATE_TEST_TMPDIR", workdir, true); | |
363 | ||
364 | qtest_add_func("/dbus-vmstate/without-list", | |
365 | test_dbus_vmstate_without_list); | |
366 | qtest_add_func("/dbus-vmstate/with-list", | |
367 | test_dbus_vmstate_with_list); | |
368 | qtest_add_func("/dbus-vmstate/only-a", | |
369 | test_dbus_vmstate_only_a); | |
370 | qtest_add_func("/dbus-vmstate/missing-src", | |
371 | test_dbus_vmstate_missing_src); | |
372 | qtest_add_func("/dbus-vmstate/missing-dst", | |
373 | test_dbus_vmstate_missing_dst); | |
374 | ||
375 | ret = g_test_run(); | |
376 | ||
377 | rmdir(workdir); | |
378 | g_free(workdir); | |
379 | ||
380 | return ret; | |
381 | } |