1 /* 2 * Test code for VMState 3 * 4 * Copyright (c) 2013 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include <glib.h> 26 27 #include "qemu-common.h" 28 #include "migration/migration.h" 29 #include "migration/vmstate.h" 30 #include "block/coroutine.h" 31 32 char temp_file[] = "/tmp/vmst.test.XXXXXX"; 33 int temp_fd; 34 35 /* Fake yield_until_fd_readable() implementation so we don't have to pull the 36 * coroutine code as dependency. 37 */ 38 void yield_until_fd_readable(int fd) 39 { 40 fd_set fds; 41 FD_ZERO(&fds); 42 FD_SET(fd, &fds); 43 select(fd + 1, &fds, NULL, NULL, NULL); 44 } 45 46 /* Duplicate temp_fd and seek to the beginning of the file */ 47 static int dup_temp_fd(bool truncate) 48 { 49 int fd = dup(temp_fd); 50 lseek(fd, 0, SEEK_SET); 51 if (truncate) { 52 g_assert_cmpint(ftruncate(fd, 0), ==, 0); 53 } 54 return fd; 55 } 56 57 typedef struct TestSruct { 58 uint32_t a, b, c, e; 59 uint64_t d, f; 60 bool skip_c_e; 61 } TestStruct; 62 63 64 static const VMStateDescription vmstate_simple = { 65 .name = "test", 66 .version_id = 1, 67 .minimum_version_id = 1, 68 .fields = (VMStateField[]) { 69 VMSTATE_UINT32(a, TestStruct), 70 VMSTATE_UINT32(b, TestStruct), 71 VMSTATE_UINT32(c, TestStruct), 72 VMSTATE_UINT64(d, TestStruct), 73 VMSTATE_END_OF_LIST() 74 } 75 }; 76 77 static void test_simple_save(void) 78 { 79 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 80 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4 }; 81 vmstate_save_state(fsave, &vmstate_simple, &obj); 82 g_assert(!qemu_file_get_error(fsave)); 83 qemu_fclose(fsave); 84 85 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 86 uint8_t expected[] = { 87 0, 0, 0, 1, /* a */ 88 0, 0, 0, 2, /* b */ 89 0, 0, 0, 3, /* c */ 90 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 91 }; 92 uint8_t result[sizeof(expected)]; 93 g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, 94 sizeof(result)); 95 g_assert(!qemu_file_get_error(loading)); 96 g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); 97 98 /* Must reach EOF */ 99 qemu_get_byte(loading); 100 g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); 101 102 qemu_fclose(loading); 103 } 104 105 static void test_simple_load(void) 106 { 107 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 108 uint8_t buf[] = { 109 0, 0, 0, 10, /* a */ 110 0, 0, 0, 20, /* b */ 111 0, 0, 0, 30, /* c */ 112 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 113 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 114 }; 115 qemu_put_buffer(fsave, buf, sizeof(buf)); 116 qemu_fclose(fsave); 117 118 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 119 TestStruct obj; 120 vmstate_load_state(loading, &vmstate_simple, &obj, 1); 121 g_assert(!qemu_file_get_error(loading)); 122 g_assert_cmpint(obj.a, ==, 10); 123 g_assert_cmpint(obj.b, ==, 20); 124 g_assert_cmpint(obj.c, ==, 30); 125 g_assert_cmpint(obj.d, ==, 40); 126 qemu_fclose(loading); 127 } 128 129 static const VMStateDescription vmstate_versioned = { 130 .name = "test", 131 .version_id = 2, 132 .minimum_version_id = 1, 133 .fields = (VMStateField[]) { 134 VMSTATE_UINT32(a, TestStruct), 135 VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so 136 * we catch bugs more easily. 137 */ 138 VMSTATE_UINT32(c, TestStruct), 139 VMSTATE_UINT64(d, TestStruct), 140 VMSTATE_UINT32_V(e, TestStruct, 2), 141 VMSTATE_UINT64_V(f, TestStruct, 2), 142 VMSTATE_END_OF_LIST() 143 } 144 }; 145 146 static void test_load_v1(void) 147 { 148 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 149 uint8_t buf[] = { 150 0, 0, 0, 10, /* a */ 151 0, 0, 0, 30, /* c */ 152 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 153 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 154 }; 155 qemu_put_buffer(fsave, buf, sizeof(buf)); 156 qemu_fclose(fsave); 157 158 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 159 TestStruct obj = { .b = 200, .e = 500, .f = 600 }; 160 vmstate_load_state(loading, &vmstate_versioned, &obj, 1); 161 g_assert(!qemu_file_get_error(loading)); 162 g_assert_cmpint(obj.a, ==, 10); 163 g_assert_cmpint(obj.b, ==, 200); 164 g_assert_cmpint(obj.c, ==, 30); 165 g_assert_cmpint(obj.d, ==, 40); 166 g_assert_cmpint(obj.e, ==, 500); 167 g_assert_cmpint(obj.f, ==, 600); 168 qemu_fclose(loading); 169 } 170 171 static void test_load_v2(void) 172 { 173 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 174 uint8_t buf[] = { 175 0, 0, 0, 10, /* a */ 176 0, 0, 0, 20, /* b */ 177 0, 0, 0, 30, /* c */ 178 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 179 0, 0, 0, 50, /* e */ 180 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 181 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 182 }; 183 qemu_put_buffer(fsave, buf, sizeof(buf)); 184 qemu_fclose(fsave); 185 186 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 187 TestStruct obj; 188 vmstate_load_state(loading, &vmstate_versioned, &obj, 2); 189 g_assert_cmpint(obj.a, ==, 10); 190 g_assert_cmpint(obj.b, ==, 20); 191 g_assert_cmpint(obj.c, ==, 30); 192 g_assert_cmpint(obj.d, ==, 40); 193 g_assert_cmpint(obj.e, ==, 50); 194 g_assert_cmpint(obj.f, ==, 60); 195 qemu_fclose(loading); 196 } 197 198 static bool test_skip(void *opaque, int version_id) 199 { 200 TestStruct *t = (TestStruct *)opaque; 201 return !t->skip_c_e; 202 } 203 204 static const VMStateDescription vmstate_skipping = { 205 .name = "test", 206 .version_id = 2, 207 .minimum_version_id = 1, 208 .fields = (VMStateField[]) { 209 VMSTATE_UINT32(a, TestStruct), 210 VMSTATE_UINT32(b, TestStruct), 211 VMSTATE_UINT32_TEST(c, TestStruct, test_skip), 212 VMSTATE_UINT64(d, TestStruct), 213 VMSTATE_UINT32_TEST(e, TestStruct, test_skip), 214 VMSTATE_UINT64_V(f, TestStruct, 2), 215 VMSTATE_END_OF_LIST() 216 } 217 }; 218 219 220 static void test_save_noskip(void) 221 { 222 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 223 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, 224 .skip_c_e = false }; 225 vmstate_save_state(fsave, &vmstate_skipping, &obj); 226 g_assert(!qemu_file_get_error(fsave)); 227 qemu_fclose(fsave); 228 229 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 230 uint8_t expected[] = { 231 0, 0, 0, 1, /* a */ 232 0, 0, 0, 2, /* b */ 233 0, 0, 0, 3, /* c */ 234 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 235 0, 0, 0, 5, /* e */ 236 0, 0, 0, 0, 0, 0, 0, 6, /* f */ 237 }; 238 uint8_t result[sizeof(expected)]; 239 g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, 240 sizeof(result)); 241 g_assert(!qemu_file_get_error(loading)); 242 g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); 243 244 /* Must reach EOF */ 245 qemu_get_byte(loading); 246 g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); 247 248 qemu_fclose(loading); 249 } 250 251 static void test_save_skip(void) 252 { 253 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 254 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, 255 .skip_c_e = true }; 256 vmstate_save_state(fsave, &vmstate_skipping, &obj); 257 g_assert(!qemu_file_get_error(fsave)); 258 qemu_fclose(fsave); 259 260 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 261 uint8_t expected[] = { 262 0, 0, 0, 1, /* a */ 263 0, 0, 0, 2, /* b */ 264 0, 0, 0, 0, 0, 0, 0, 4, /* d */ 265 0, 0, 0, 0, 0, 0, 0, 6, /* f */ 266 }; 267 uint8_t result[sizeof(expected)]; 268 g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==, 269 sizeof(result)); 270 g_assert(!qemu_file_get_error(loading)); 271 g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0); 272 273 274 /* Must reach EOF */ 275 qemu_get_byte(loading); 276 g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO); 277 278 qemu_fclose(loading); 279 } 280 281 static void test_load_noskip(void) 282 { 283 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 284 uint8_t buf[] = { 285 0, 0, 0, 10, /* a */ 286 0, 0, 0, 20, /* b */ 287 0, 0, 0, 30, /* c */ 288 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 289 0, 0, 0, 50, /* e */ 290 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 291 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 292 }; 293 qemu_put_buffer(fsave, buf, sizeof(buf)); 294 qemu_fclose(fsave); 295 296 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 297 TestStruct obj = { .skip_c_e = false }; 298 vmstate_load_state(loading, &vmstate_skipping, &obj, 2); 299 g_assert(!qemu_file_get_error(loading)); 300 g_assert_cmpint(obj.a, ==, 10); 301 g_assert_cmpint(obj.b, ==, 20); 302 g_assert_cmpint(obj.c, ==, 30); 303 g_assert_cmpint(obj.d, ==, 40); 304 g_assert_cmpint(obj.e, ==, 50); 305 g_assert_cmpint(obj.f, ==, 60); 306 qemu_fclose(loading); 307 } 308 309 static void test_load_skip(void) 310 { 311 QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb"); 312 uint8_t buf[] = { 313 0, 0, 0, 10, /* a */ 314 0, 0, 0, 20, /* b */ 315 0, 0, 0, 0, 0, 0, 0, 40, /* d */ 316 0, 0, 0, 0, 0, 0, 0, 60, /* f */ 317 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 318 }; 319 qemu_put_buffer(fsave, buf, sizeof(buf)); 320 qemu_fclose(fsave); 321 322 QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb"); 323 TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; 324 vmstate_load_state(loading, &vmstate_skipping, &obj, 2); 325 g_assert(!qemu_file_get_error(loading)); 326 g_assert_cmpint(obj.a, ==, 10); 327 g_assert_cmpint(obj.b, ==, 20); 328 g_assert_cmpint(obj.c, ==, 300); 329 g_assert_cmpint(obj.d, ==, 40); 330 g_assert_cmpint(obj.e, ==, 500); 331 g_assert_cmpint(obj.f, ==, 60); 332 qemu_fclose(loading); 333 } 334 335 int main(int argc, char **argv) 336 { 337 temp_fd = mkstemp(temp_file); 338 339 g_test_init(&argc, &argv, NULL); 340 g_test_add_func("/vmstate/simple/save", test_simple_save); 341 g_test_add_func("/vmstate/simple/load", test_simple_load); 342 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); 343 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); 344 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); 345 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip); 346 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip); 347 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip); 348 g_test_run(); 349 350 close(temp_fd); 351 unlink(temp_file); 352 353 return 0; 354 } 355