qemu/tests/test-vmstate.c
Juan Quintela 35d08458a9 savevm: Remove all the unneeded version_minimum_id_old (rest)
After previous Peter patch, they are redundant.  This way we don't
assign them except when needed.  Once there, there were lots of case
where the ".fields" indentation was wrong:

     .fields = (VMStateField []) {
and
     .fields =      (VMStateField []) {

Change all the combinations to:

     .fields = (VMStateField[]){

The biggest problem (appart from aesthetics) was that checkpatch complained
when we copy&pasted the code from one place to another.

Signed-off-by: Juan Quintela <quintela@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
2014-05-14 15:24:51 +02:00

355 lines
11 KiB
C

/*
* Test code for VMState
*
* Copyright (c) 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <glib.h>
#include "qemu-common.h"
#include "migration/migration.h"
#include "migration/vmstate.h"
#include "block/coroutine.h"
char temp_file[] = "/tmp/vmst.test.XXXXXX";
int temp_fd;
/* Fake yield_until_fd_readable() implementation so we don't have to pull the
* coroutine code as dependency.
*/
void yield_until_fd_readable(int fd)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
select(fd + 1, &fds, NULL, NULL, NULL);
}
/* Duplicate temp_fd and seek to the beginning of the file */
static int dup_temp_fd(bool truncate)
{
int fd = dup(temp_fd);
lseek(fd, 0, SEEK_SET);
if (truncate) {
g_assert_cmpint(ftruncate(fd, 0), ==, 0);
}
return fd;
}
typedef struct TestSruct {
uint32_t a, b, c, e;
uint64_t d, f;
bool skip_c_e;
} TestStruct;
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 test_simple_save(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
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 *loading = qemu_fdopen(dup_temp_fd(false), "rb");
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)), ==,
sizeof(result));
g_assert(!qemu_file_get_error(loading));
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
/* Must reach EOF */
qemu_get_byte(loading);
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
qemu_fclose(loading);
}
static void test_simple_load(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
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 */
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 = qemu_fdopen(dup_temp_fd(false), "rb");
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 const VMStateDescription vmstate_versioned = {
.name = "test",
.version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(a, TestStruct),
VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so
* we catch bugs more easily.
*/
VMSTATE_UINT32(c, TestStruct),
VMSTATE_UINT64(d, TestStruct),
VMSTATE_UINT32_V(e, TestStruct, 2),
VMSTATE_UINT64_V(f, TestStruct, 2),
VMSTATE_END_OF_LIST()
}
};
static void test_load_v1(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 30, /* c */
0, 0, 0, 0, 0, 0, 0, 40, /* d */
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 = qemu_fdopen(dup_temp_fd(false), "rb");
TestStruct obj = { .b = 200, .e = 500, .f = 600 };
vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
g_assert(!qemu_file_get_error(loading));
g_assert_cmpint(obj.a, ==, 10);
g_assert_cmpint(obj.b, ==, 200);
g_assert_cmpint(obj.c, ==, 30);
g_assert_cmpint(obj.d, ==, 40);
g_assert_cmpint(obj.e, ==, 500);
g_assert_cmpint(obj.f, ==, 600);
qemu_fclose(loading);
}
static void test_load_v2(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
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 */
0, 0, 0, 50, /* e */
0, 0, 0, 0, 0, 0, 0, 60, /* f */
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 = qemu_fdopen(dup_temp_fd(false), "rb");
TestStruct obj;
vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
g_assert_cmpint(obj.a, ==, 10);
g_assert_cmpint(obj.b, ==, 20);
g_assert_cmpint(obj.c, ==, 30);
g_assert_cmpint(obj.d, ==, 40);
g_assert_cmpint(obj.e, ==, 50);
g_assert_cmpint(obj.f, ==, 60);
qemu_fclose(loading);
}
static bool test_skip(void *opaque, int version_id)
{
TestStruct *t = (TestStruct *)opaque;
return !t->skip_c_e;
}
static const VMStateDescription vmstate_skipping = {
.name = "test",
.version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(a, TestStruct),
VMSTATE_UINT32(b, TestStruct),
VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
VMSTATE_UINT64(d, TestStruct),
VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
VMSTATE_UINT64_V(f, TestStruct, 2),
VMSTATE_END_OF_LIST()
}
};
static void test_save_noskip(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
.skip_c_e = false };
vmstate_save_state(fsave, &vmstate_skipping, &obj);
g_assert(!qemu_file_get_error(fsave));
qemu_fclose(fsave);
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
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 */
0, 0, 0, 5, /* e */
0, 0, 0, 0, 0, 0, 0, 6, /* f */
};
uint8_t result[sizeof(expected)];
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
sizeof(result));
g_assert(!qemu_file_get_error(loading));
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
/* Must reach EOF */
qemu_get_byte(loading);
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
qemu_fclose(loading);
}
static void test_save_skip(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
.skip_c_e = true };
vmstate_save_state(fsave, &vmstate_skipping, &obj);
g_assert(!qemu_file_get_error(fsave));
qemu_fclose(fsave);
QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
uint8_t expected[] = {
0, 0, 0, 1, /* a */
0, 0, 0, 2, /* b */
0, 0, 0, 0, 0, 0, 0, 4, /* d */
0, 0, 0, 0, 0, 0, 0, 6, /* f */
};
uint8_t result[sizeof(expected)];
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
sizeof(result));
g_assert(!qemu_file_get_error(loading));
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
/* Must reach EOF */
qemu_get_byte(loading);
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
qemu_fclose(loading);
}
static void test_load_noskip(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
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 */
0, 0, 0, 50, /* e */
0, 0, 0, 0, 0, 0, 0, 60, /* f */
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 = qemu_fdopen(dup_temp_fd(false), "rb");
TestStruct obj = { .skip_c_e = false };
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
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);
g_assert_cmpint(obj.e, ==, 50);
g_assert_cmpint(obj.f, ==, 60);
qemu_fclose(loading);
}
static void test_load_skip(void)
{
QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
0, 0, 0, 0, 0, 0, 0, 40, /* d */
0, 0, 0, 0, 0, 0, 0, 60, /* f */
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 = qemu_fdopen(dup_temp_fd(false), "rb");
TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
g_assert(!qemu_file_get_error(loading));
g_assert_cmpint(obj.a, ==, 10);
g_assert_cmpint(obj.b, ==, 20);
g_assert_cmpint(obj.c, ==, 300);
g_assert_cmpint(obj.d, ==, 40);
g_assert_cmpint(obj.e, ==, 500);
g_assert_cmpint(obj.f, ==, 60);
qemu_fclose(loading);
}
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/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);
g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
g_test_run();
close(temp_fd);
unlink(temp_file);
return 0;
}