qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

dbus-vmstate-test.c (10289B)


      1 #include "qemu/osdep.h"
      2 #include <glib/gstdio.h>
      3 #include <gio/gio.h>
      4 #include "libqtest.h"
      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);
    236         qtest_set_expected_status(dst_qemu, EXIT_FAILURE);
    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 }