Commit 4ea7df4e authored by Juan Quintela's avatar Juan Quintela
Browse files

vmstate: Refactor & increase tests for primitive types



This commit refactor the simple tests to test all integer types. We
move to hex because it is easier to read values of different types.

Signed-off-by: default avatarJuan Quintela <quintela@redhat.com>
Reviewed-by: default avatarDr. David Alan Gilbert <dgilbert@redhat.com>
parent 13cde508
Loading
Loading
Loading
Loading
+212 −61
Original line number Diff line number Diff line
@@ -54,80 +54,232 @@ static QEMUFile *open_test_file(bool write)
    return qemu_fdopen(fd, write ? "wb" : "rb");
}

typedef struct TestSruct {
    uint32_t a, b, c, e;
    uint64_t d, f;
    bool skip_c_e;
} TestStruct;
#define SUCCESS(val) \
    g_assert_cmpint((val), ==, 0)

#define FAILURE(val) \
    g_assert_cmpint((val), !=, 0)

static const VMStateDescription vmstate_simple = {
    .name = "test",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(a, TestStruct),
        VMSTATE_UINT32(b, TestStruct),
        VMSTATE_UINT32(c, TestStruct),
        VMSTATE_UINT64(d, TestStruct),
        VMSTATE_END_OF_LIST()
static void save_vmstate(const VMStateDescription *desc, void *obj)
{
    QEMUFile *f = open_test_file(true);

    /* Save file with vmstate */
    vmstate_save_state(f, desc, obj);
    qemu_put_byte(f, QEMU_VM_EOF);
    g_assert(!qemu_file_get_error(f));
    qemu_fclose(f);
}
};

static void test_simple_save(void)
static void compare_vmstate(uint8_t *wire, size_t size)
{
    QEMUFile *fsave = open_test_file(true);
    TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4 };
    vmstate_save_state(fsave, &vmstate_simple, &obj);
    g_assert(!qemu_file_get_error(fsave));
    qemu_fclose(fsave);
    QEMUFile *f = open_test_file(false);
    uint8_t result[size];

    QEMUFile *loading = open_test_file(false);
    uint8_t expected[] = {
        0, 0, 0, 1, /* a */
        0, 0, 0, 2, /* b */
        0, 0, 0, 3, /* c */
        0, 0, 0, 0, 0, 0, 0, 4, /* d */
    };
    uint8_t result[sizeof(expected)];
    g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
    /* read back as binary */

    g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==,
                    sizeof(result));
    g_assert(!qemu_file_get_error(loading));
    g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
    g_assert(!qemu_file_get_error(f));

    /* Compare that what is on the file is the same that what we
       expected to be there */
    SUCCESS(memcmp(result, wire, sizeof(result)));

    /* Must reach EOF */
    qemu_get_byte(loading);
    g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
    qemu_get_byte(f);
    g_assert_cmpint(qemu_file_get_error(f), ==, -EIO);

    qemu_fclose(loading);
    qemu_fclose(f);
}

static void test_simple_load(void)
static int load_vmstate_one(const VMStateDescription *desc, void *obj,
                            int version, uint8_t *wire, size_t size)
{
    QEMUFile *fsave = open_test_file(true);
    uint8_t buf[] = {
        0, 0, 0, 10,             /* a */
        0, 0, 0, 20,             /* b */
        0, 0, 0, 30,             /* c */
        0, 0, 0, 0, 0, 0, 0, 40, /* d */
    QEMUFile *f;
    int ret;

    f = open_test_file(true);
    qemu_put_buffer(f, wire, size);
    qemu_fclose(f);

    f = open_test_file(false);
    ret = vmstate_load_state(f, desc, obj, version);
    if (ret) {
        g_assert(qemu_file_get_error(f));
    } else{
        g_assert(!qemu_file_get_error(f));
    }
    qemu_fclose(f);
    return ret;
}


static int load_vmstate(const VMStateDescription *desc,
                        void *obj, void *obj_clone,
                        void (*obj_copy)(void *, void*),
                        int version, uint8_t *wire, size_t size)
{
    /* We test with zero size */
    obj_copy(obj_clone, obj);
    FAILURE(load_vmstate_one(desc, obj, version, wire, 0));

    /* Stream ends with QEMU_EOF, so we need at least 3 bytes to be
     * able to test in the middle */

    if (size > 3) {

        /* We test with size - 2. We can't test size - 1 due to EOF tricks */
        obj_copy(obj, obj_clone);
        FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2));

        /* Test with size/2, first half of real state */
        obj_copy(obj, obj_clone);
        FAILURE(load_vmstate_one(desc, obj, version, wire, size/2));

        /* Test with size/2, second half of real state */
        obj_copy(obj, obj_clone);
        FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2));

    }
    obj_copy(obj, obj_clone);
    return load_vmstate_one(desc, obj, version, wire, size);
}

/* Test struct that we are going to use for our tests */

typedef struct TestSimple {
    bool     b_1,   b_2;
    uint8_t  u8_1;
    uint16_t u16_1;
    uint32_t u32_1;
    uint64_t u64_1;
    int8_t   i8_1,  i8_2;
    int16_t  i16_1, i16_2;
    int32_t  i32_1, i32_2;
    int64_t  i64_1, i64_2;
} TestSimple;

/* Object instantiation, we are going to use it in more than one test */

TestSimple obj_simple = {
    .b_1 = true,
    .b_2 = false,
    .u8_1 = 130,
    .u16_1 = 512,
    .u32_1 = 70000,
    .u64_1 = 12121212,
    .i8_1 = 65,
    .i8_2 = -65,
    .i16_1 = 512,
    .i16_2 = -512,
    .i32_1 = 70000,
    .i32_2 = -70000,
    .i64_1 = 12121212,
    .i64_2 = -12121212,
};

/* Description of the values.  If you add a primitive type
   you are expected to add a test here */

static const VMStateDescription vmstate_simple_primitive = {
    .name = "simple/primitive",
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_BOOL(b_1, TestSimple),
        VMSTATE_BOOL(b_2, TestSimple),
        VMSTATE_UINT8(u8_1, TestSimple),
        VMSTATE_UINT16(u16_1, TestSimple),
        VMSTATE_UINT32(u32_1, TestSimple),
        VMSTATE_UINT64(u64_1, TestSimple),
        VMSTATE_INT8(i8_1, TestSimple),
        VMSTATE_INT8(i8_2, TestSimple),
        VMSTATE_INT16(i16_1, TestSimple),
        VMSTATE_INT16(i16_2, TestSimple),
        VMSTATE_INT32(i32_1, TestSimple),
        VMSTATE_INT32(i32_2, TestSimple),
        VMSTATE_INT64(i64_1, TestSimple),
        VMSTATE_INT64(i64_2, TestSimple),
        VMSTATE_END_OF_LIST()
    }
};

/* It describes what goes through the wire.  Our tests are basically:

   * save test
     - save a struct a vmstate to a file
     - read that file back (binary read, no vmstate)
     - compare it with what we expect to be on the wire
   * load test
     - save to the file what we expect to be on the wire
     - read struct back with vmstate in a different
     - compare back with the original struct
*/

uint8_t wire_simple_primitive[] = {
    /* b_1 */   0x01,
    /* b_2 */   0x00,
    /* u8_1 */  0x82,
    /* u16_1 */ 0x02, 0x00,
    /* u32_1 */ 0x00, 0x01, 0x11, 0x70,
    /* u64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
    /* i8_1 */  0x41,
    /* i8_2 */  0xbf,
    /* i16_1 */ 0x02, 0x00,
    /* i16_2 */ 0xfe, 0x0,
    /* i32_1 */ 0x00, 0x01, 0x11, 0x70,
    /* i32_2 */ 0xff, 0xfe, 0xee, 0x90,
    /* i64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
    /* i64_2 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84,
    QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
};
    qemu_put_buffer(fsave, buf, sizeof(buf));
    qemu_fclose(fsave);

    QEMUFile *loading = open_test_file(false);
    TestStruct obj;
    vmstate_load_state(loading, &vmstate_simple, &obj, 1);
    g_assert(!qemu_file_get_error(loading));
    g_assert_cmpint(obj.a, ==, 10);
    g_assert_cmpint(obj.b, ==, 20);
    g_assert_cmpint(obj.c, ==, 30);
    g_assert_cmpint(obj.d, ==, 40);
    qemu_fclose(loading);
static void obj_simple_copy(void *target, void *source)
{
    memcpy(target, source, sizeof(TestSimple));
}

static void test_simple_primitive(void)
{
    TestSimple obj, obj_clone;

    memset(&obj, 0, sizeof(obj));
    save_vmstate(&vmstate_simple_primitive, &obj_simple);

    compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive));

    SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone,
                         obj_simple_copy, 1, wire_simple_primitive,
                         sizeof(wire_simple_primitive)));

#define FIELD_EQUAL(name)   g_assert_cmpint(obj.name, ==, obj_simple.name)

    FIELD_EQUAL(b_1);
    FIELD_EQUAL(b_2);
    FIELD_EQUAL(u8_1);
    FIELD_EQUAL(u16_1);
    FIELD_EQUAL(u32_1);
    FIELD_EQUAL(u64_1);
    FIELD_EQUAL(i8_1);
    FIELD_EQUAL(i8_2);
    FIELD_EQUAL(i16_1);
    FIELD_EQUAL(i16_2);
    FIELD_EQUAL(i32_1);
    FIELD_EQUAL(i32_2);
    FIELD_EQUAL(i64_1);
    FIELD_EQUAL(i64_2);
}
#undef FIELD_EQUAL

typedef struct TestStruct {
    uint32_t a, b, c, e;
    uint64_t d, f;
    bool skip_c_e;
} TestStruct;

static const VMStateDescription vmstate_versioned = {
    .name = "test",
    .name = "test/versioned",
    .version_id = 2,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
@@ -202,7 +354,7 @@ static bool test_skip(void *opaque, int version_id)
}

static const VMStateDescription vmstate_skipping = {
    .name = "test",
    .name = "test/skip",
    .version_id = 2,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
@@ -337,8 +489,7 @@ int main(int argc, char **argv)
    temp_fd = mkstemp(temp_file);

    g_test_init(&argc, &argv, NULL);
    g_test_add_func("/vmstate/simple/save", test_simple_save);
    g_test_add_func("/vmstate/simple/load", test_simple_load);
    g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
    g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
    g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
    g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);