xref: /openbmc/qemu/rust/qemu-api/tests/vmstate_tests.rs (revision e4fb0be1d1d6b67df7709d84d16133b64f455ce8)
1 // Copyright (C) 2025 Intel Corporation.
2 // Author(s): Zhao Liu <zhai1.liu@intel.com>
3 // SPDX-License-Identifier: GPL-2.0-or-later
4 
5 use std::{
6     ffi::{c_void, CStr},
7     mem::size_of,
8     ptr::NonNull,
9     slice,
10 };
11 
12 use qemu_api::{
13     bindings::{
14         vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
15         vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
16     },
17     c_str,
18     cell::{BqlCell, Opaque},
19     impl_vmstate_forward,
20     vmstate::{VMStateDescription, VMStateField},
21     vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
22     zeroable::Zeroable,
23 };
24 
25 const FOO_ARRAY_MAX: usize = 3;
26 
27 // =========================== Test VMSTATE_FOOA ===========================
28 // Test the use cases of the vmstate macro, corresponding to the following C
29 // macro variants:
30 //   * VMSTATE_FOOA:
31 //     - VMSTATE_U16
32 //     - VMSTATE_UNUSED
33 //     - VMSTATE_VARRAY_UINT16_UNSAFE
34 //     - VMSTATE_VARRAY_MULTIPLY
35 #[repr(C)]
36 #[derive(Default, qemu_api_macros::offsets)]
37 struct FooA {
38     arr: [u8; FOO_ARRAY_MAX],
39     num: u16,
40     arr_mul: [i8; FOO_ARRAY_MAX],
41     num_mul: u32,
42     elem: i8,
43 }
44 
45 static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
46     name: c_str!("foo_a").as_ptr(),
47     version_id: 1,
48     minimum_version_id: 1,
49     fields: vmstate_fields! {
50         vmstate_of!(FooA, elem),
51         vmstate_unused!(size_of::<i64>()),
52         vmstate_of!(FooA, arr[0 .. num]).with_version_id(0),
53         vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]),
54     },
55     ..Zeroable::ZERO
56 };
57 
58 #[test]
59 fn test_vmstate_uint16() {
60     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
61 
62     // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16)
63     assert_eq!(
64         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
65         b"elem\0"
66     );
67     assert_eq!(foo_fields[0].offset, 16);
68     assert_eq!(foo_fields[0].num_offset, 0);
69     assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 });
70     assert_eq!(foo_fields[0].version_id, 0);
71     assert_eq!(foo_fields[0].size, 1);
72     assert_eq!(foo_fields[0].num, 0);
73     assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE);
74     assert!(foo_fields[0].vmsd.is_null());
75     assert!(foo_fields[0].field_exists.is_none());
76 }
77 
78 #[test]
79 fn test_vmstate_unused() {
80     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
81 
82     // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED)
83     assert_eq!(
84         unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
85         b"unused\0"
86     );
87     assert_eq!(foo_fields[1].offset, 0);
88     assert_eq!(foo_fields[1].num_offset, 0);
89     assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer });
90     assert_eq!(foo_fields[1].version_id, 0);
91     assert_eq!(foo_fields[1].size, 8);
92     assert_eq!(foo_fields[1].num, 0);
93     assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER);
94     assert!(foo_fields[1].vmsd.is_null());
95     assert!(foo_fields[1].field_exists.is_none());
96 }
97 
98 #[test]
99 fn test_vmstate_varray_uint16_unsafe() {
100     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
101 
102     // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to
103     // VMSTATE_VARRAY_UINT16_UNSAFE)
104     assert_eq!(
105         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
106         b"arr\0"
107     );
108     assert_eq!(foo_fields[2].offset, 0);
109     assert_eq!(foo_fields[2].num_offset, 4);
110     assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
111     assert_eq!(foo_fields[2].version_id, 0);
112     assert_eq!(foo_fields[2].size, 1);
113     assert_eq!(foo_fields[2].num, 0);
114     assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16);
115     assert!(foo_fields[2].vmsd.is_null());
116     assert!(foo_fields[2].field_exists.is_none());
117 }
118 
119 #[test]
120 fn test_vmstate_varray_multiply() {
121     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
122 
123     // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to
124     // VMSTATE_VARRAY_MULTIPLY)
125     assert_eq!(
126         unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
127         b"arr_mul\0"
128     );
129     assert_eq!(foo_fields[3].offset, 6);
130     assert_eq!(foo_fields[3].num_offset, 12);
131     assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 });
132     assert_eq!(foo_fields[3].version_id, 0);
133     assert_eq!(foo_fields[3].size, 1);
134     assert_eq!(foo_fields[3].num, 16);
135     assert_eq!(
136         foo_fields[3].flags.0,
137         VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
138     );
139     assert!(foo_fields[3].vmsd.is_null());
140     assert!(foo_fields[3].field_exists.is_none());
141 
142     // The last VMStateField in VMSTATE_FOOA.
143     assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
144 }
145 
146 // =========================== Test VMSTATE_FOOB ===========================
147 // Test the use cases of the vmstate macro, corresponding to the following C
148 // macro variants:
149 //   * VMSTATE_FOOB:
150 //     - VMSTATE_BOOL_V
151 //     - VMSTATE_U64
152 //     - VMSTATE_STRUCT_VARRAY_UINT8
153 //     - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32
154 //     - VMSTATE_ARRAY
155 //     - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn
156 #[repr(C)]
157 #[derive(Default, qemu_api_macros::offsets)]
158 struct FooB {
159     arr_a: [FooA; FOO_ARRAY_MAX],
160     num_a: u8,
161     arr_a_mul: [FooA; FOO_ARRAY_MAX],
162     num_a_mul: u32,
163     wrap: BqlCell<u64>,
164     val: bool,
165     // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test.
166     arr_i64: [i64; FOO_ARRAY_MAX],
167     arr_a_wrap: [FooA; FOO_ARRAY_MAX],
168     num_a_wrap: BqlCell<u32>,
169 }
170 
171 fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
172     true
173 }
174 
175 static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
176     name: c_str!("foo_b").as_ptr(),
177     version_id: 2,
178     minimum_version_id: 1,
179     fields: vmstate_fields! {
180         vmstate_of!(FooB, val).with_version_id(2),
181         vmstate_of!(FooB, wrap),
182         vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
183         vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
184         vmstate_of!(FooB, arr_i64),
185         vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
186     },
187     ..Zeroable::ZERO
188 };
189 
190 #[test]
191 fn test_vmstate_bool_v() {
192     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
193 
194     // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V)
195     assert_eq!(
196         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
197         b"val\0"
198     );
199     assert_eq!(foo_fields[0].offset, 136);
200     assert_eq!(foo_fields[0].num_offset, 0);
201     assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool });
202     assert_eq!(foo_fields[0].version_id, 2);
203     assert_eq!(foo_fields[0].size, 1);
204     assert_eq!(foo_fields[0].num, 0);
205     assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE);
206     assert!(foo_fields[0].vmsd.is_null());
207     assert!(foo_fields[0].field_exists.is_none());
208 }
209 
210 #[test]
211 fn test_vmstate_uint64() {
212     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
213 
214     // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64)
215     assert_eq!(
216         unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
217         b"wrap\0"
218     );
219     assert_eq!(foo_fields[1].offset, 128);
220     assert_eq!(foo_fields[1].num_offset, 0);
221     assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 });
222     assert_eq!(foo_fields[1].version_id, 0);
223     assert_eq!(foo_fields[1].size, 8);
224     assert_eq!(foo_fields[1].num, 0);
225     assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE);
226     assert!(foo_fields[1].vmsd.is_null());
227     assert!(foo_fields[1].field_exists.is_none());
228 }
229 
230 #[test]
231 fn test_vmstate_struct_varray_uint8() {
232     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
233 
234     // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to
235     // VMSTATE_STRUCT_VARRAY_UINT8)
236     assert_eq!(
237         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
238         b"arr_a\0"
239     );
240     assert_eq!(foo_fields[2].offset, 0);
241     assert_eq!(foo_fields[2].num_offset, 60);
242     assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field.
243     assert_eq!(foo_fields[2].version_id, 1);
244     assert_eq!(foo_fields[2].size, 20);
245     assert_eq!(foo_fields[2].num, 0);
246     assert_eq!(
247         foo_fields[2].flags.0,
248         VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0
249     );
250     assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA);
251     assert!(foo_fields[2].field_exists.is_none());
252 }
253 
254 #[test]
255 fn test_vmstate_struct_varray_uint32_multiply() {
256     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
257 
258     // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to
259     // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32)
260     assert_eq!(
261         unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
262         b"arr_a_mul\0"
263     );
264     assert_eq!(foo_fields[3].offset, 64);
265     assert_eq!(foo_fields[3].num_offset, 124);
266     assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field.
267     assert_eq!(foo_fields[3].version_id, 2);
268     assert_eq!(foo_fields[3].size, 20);
269     assert_eq!(foo_fields[3].num, 32);
270     assert_eq!(
271         foo_fields[3].flags.0,
272         VMStateFlags::VMS_STRUCT.0
273             | VMStateFlags::VMS_VARRAY_UINT32.0
274             | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
275     );
276     assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA);
277     assert!(foo_fields[3].field_exists.is_none());
278 }
279 
280 #[test]
281 fn test_vmstate_macro_array() {
282     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
283 
284     // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to
285     // VMSTATE_ARRAY)
286     assert_eq!(
287         unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(),
288         b"arr_i64\0"
289     );
290     assert_eq!(foo_fields[4].offset, 144);
291     assert_eq!(foo_fields[4].num_offset, 0);
292     assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 });
293     assert_eq!(foo_fields[4].version_id, 0);
294     assert_eq!(foo_fields[4].size, 8);
295     assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32);
296     assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY);
297     assert!(foo_fields[4].vmsd.is_null());
298     assert!(foo_fields[4].field_exists.is_none());
299 }
300 
301 #[test]
302 fn test_vmstate_struct_varray_uint8_wrapper() {
303     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
304     let mut foo_b: FooB = Default::default();
305     let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::<c_void>();
306 
307     // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to
308     // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in
309     // test_vmstate_struct_varray_uint8.
310     assert_eq!(
311         unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(),
312         b"arr_a_wrap\0"
313     );
314     assert_eq!(foo_fields[5].num_offset, 228);
315     assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) });
316 
317     // The last VMStateField in VMSTATE_FOOB.
318     assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END);
319 }
320 
321 // =========================== Test VMSTATE_FOOC ===========================
322 // Test the use cases of the vmstate macro, corresponding to the following C
323 // macro variants:
324 //   * VMSTATE_FOOC:
325 //     - VMSTATE_POINTER
326 //     - VMSTATE_ARRAY_OF_POINTER
327 struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible.
328 
329 impl_vmstate_forward!(FooCWrapper);
330 
331 #[repr(C)]
332 #[derive(qemu_api_macros::offsets)]
333 struct FooC {
334     ptr: *const i32,
335     ptr_a: NonNull<FooA>,
336     arr_ptr: [Box<u8>; FOO_ARRAY_MAX],
337     arr_ptr_wrap: FooCWrapper,
338 }
339 
340 static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
341     name: c_str!("foo_c").as_ptr(),
342     version_id: 3,
343     minimum_version_id: 1,
344     fields: vmstate_fields! {
345         vmstate_of!(FooC, ptr).with_version_id(2),
346         // FIXME: Currently vmstate_struct doesn't support the pointer to structure.
347         // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
348         vmstate_unused!(size_of::<NonNull<FooA>>()),
349         vmstate_of!(FooC, arr_ptr),
350         vmstate_of!(FooC, arr_ptr_wrap),
351     },
352     ..Zeroable::ZERO
353 };
354 
355 const PTR_SIZE: usize = size_of::<*mut ()>();
356 
357 #[test]
358 fn test_vmstate_pointer() {
359     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
360 
361     // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER)
362     assert_eq!(
363         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
364         b"ptr\0"
365     );
366     assert_eq!(foo_fields[0].offset, 0);
367     assert_eq!(foo_fields[0].num_offset, 0);
368     assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 });
369     assert_eq!(foo_fields[0].version_id, 2);
370     assert_eq!(foo_fields[0].size, 4);
371     assert_eq!(foo_fields[0].num, 0);
372     assert_eq!(
373         foo_fields[0].flags.0,
374         VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0
375     );
376     assert!(foo_fields[0].vmsd.is_null());
377     assert!(foo_fields[0].field_exists.is_none());
378 }
379 
380 #[test]
381 fn test_vmstate_macro_array_of_pointer() {
382     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
383 
384     // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to
385     // VMSTATE_ARRAY_OF_POINTER)
386     assert_eq!(
387         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
388         b"arr_ptr\0"
389     );
390     assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE);
391     assert_eq!(foo_fields[2].num_offset, 0);
392     assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
393     assert_eq!(foo_fields[2].version_id, 0);
394     assert_eq!(foo_fields[2].size, PTR_SIZE);
395     assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32);
396     assert_eq!(
397         foo_fields[2].flags.0,
398         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
399     );
400     assert!(foo_fields[2].vmsd.is_null());
401     assert!(foo_fields[2].field_exists.is_none());
402 }
403 
404 #[test]
405 fn test_vmstate_macro_array_of_pointer_wrapped() {
406     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
407 
408     // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to
409     // VMSTATE_ARRAY_OF_POINTER)
410     assert_eq!(
411         unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
412         b"arr_ptr_wrap\0"
413     );
414     assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE);
415     assert_eq!(foo_fields[3].num_offset, 0);
416     assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 });
417     assert_eq!(foo_fields[3].version_id, 0);
418     assert_eq!(foo_fields[3].size, PTR_SIZE);
419     assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32);
420     assert_eq!(
421         foo_fields[3].flags.0,
422         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
423     );
424     assert!(foo_fields[3].vmsd.is_null());
425     assert!(foo_fields[3].field_exists.is_none());
426 
427     // The last VMStateField in VMSTATE_FOOC.
428     assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
429 }
430 
431 // =========================== Test VMSTATE_FOOD ===========================
432 // Test the use cases of the vmstate macro, corresponding to the following C
433 // macro variants:
434 //   * VMSTATE_FOOD:
435 //     - VMSTATE_VALIDATE
436 
437 // Add more member fields when vmstate_of/vmstate_struct support "test"
438 // parameter.
439 struct FooD;
440 
441 impl FooD {
442     fn validate_food_0(&self, _version_id: u8) -> bool {
443         true
444     }
445 
446     fn validate_food_1(_state: &FooD, _version_id: u8) -> bool {
447         false
448     }
449 }
450 
451 fn validate_food_2(_state: &FooD, _version_id: u8) -> bool {
452     true
453 }
454 
455 static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
456     name: c_str!("foo_d").as_ptr(),
457     version_id: 3,
458     minimum_version_id: 1,
459     fields: vmstate_fields! {
460         vmstate_validate!(FooD, c_str!("foo_d_0"), FooD::validate_food_0),
461         vmstate_validate!(FooD, c_str!("foo_d_1"), FooD::validate_food_1),
462         vmstate_validate!(FooD, c_str!("foo_d_2"), validate_food_2),
463     },
464     ..Zeroable::ZERO
465 };
466 
467 #[test]
468 fn test_vmstate_validate() {
469     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) };
470     let mut foo_d = FooD;
471     let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>();
472 
473     // 1st VMStateField in VMSTATE_FOOD
474     assert_eq!(
475         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
476         b"foo_d_0\0"
477     );
478     assert_eq!(foo_fields[0].offset, 0);
479     assert_eq!(foo_fields[0].num_offset, 0);
480     assert!(foo_fields[0].info.is_null());
481     assert_eq!(foo_fields[0].version_id, 0);
482     assert_eq!(foo_fields[0].size, 0);
483     assert_eq!(foo_fields[0].num, 0);
484     assert_eq!(
485         foo_fields[0].flags.0,
486         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0
487     );
488     assert!(foo_fields[0].vmsd.is_null());
489     assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) });
490 
491     // 2nd VMStateField in VMSTATE_FOOD
492     assert_eq!(
493         unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
494         b"foo_d_1\0"
495     );
496     assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) });
497 
498     // 3rd VMStateField in VMSTATE_FOOD
499     assert_eq!(
500         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
501         b"foo_d_2\0"
502     );
503     assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) });
504 
505     // The last VMStateField in VMSTATE_FOOD.
506     assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END);
507 }
508