xref: /openbmc/qemu/rust/qemu-api/tests/vmstate_tests.rs (revision b134a09ffab3b918979007cf40f603e5b54ed597)
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)]
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)]
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 struct FooC {
333     ptr: *const i32,
334     ptr_a: NonNull<FooA>,
335     arr_ptr: [Box<u8>; FOO_ARRAY_MAX],
336     arr_ptr_wrap: FooCWrapper,
337 }
338 
339 static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
340     name: c_str!("foo_c").as_ptr(),
341     version_id: 3,
342     minimum_version_id: 1,
343     fields: vmstate_fields! {
344         vmstate_of!(FooC, ptr).with_version_id(2),
345         // FIXME: Currently vmstate_struct doesn't support the pointer to structure.
346         // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
347         vmstate_unused!(size_of::<NonNull<FooA>>()),
348         vmstate_of!(FooC, arr_ptr),
349         vmstate_of!(FooC, arr_ptr_wrap),
350     },
351     ..Zeroable::ZERO
352 };
353 
354 const PTR_SIZE: usize = size_of::<*mut ()>();
355 
356 #[test]
357 fn test_vmstate_pointer() {
358     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
359 
360     // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER)
361     assert_eq!(
362         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
363         b"ptr\0"
364     );
365     assert_eq!(foo_fields[0].offset, 0);
366     assert_eq!(foo_fields[0].num_offset, 0);
367     assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 });
368     assert_eq!(foo_fields[0].version_id, 2);
369     assert_eq!(foo_fields[0].size, 4);
370     assert_eq!(foo_fields[0].num, 0);
371     assert_eq!(
372         foo_fields[0].flags.0,
373         VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0
374     );
375     assert!(foo_fields[0].vmsd.is_null());
376     assert!(foo_fields[0].field_exists.is_none());
377 }
378 
379 #[test]
380 fn test_vmstate_macro_array_of_pointer() {
381     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
382 
383     // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to
384     // VMSTATE_ARRAY_OF_POINTER)
385     assert_eq!(
386         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
387         b"arr_ptr\0"
388     );
389     assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE);
390     assert_eq!(foo_fields[2].num_offset, 0);
391     assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
392     assert_eq!(foo_fields[2].version_id, 0);
393     assert_eq!(foo_fields[2].size, PTR_SIZE);
394     assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32);
395     assert_eq!(
396         foo_fields[2].flags.0,
397         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
398     );
399     assert!(foo_fields[2].vmsd.is_null());
400     assert!(foo_fields[2].field_exists.is_none());
401 }
402 
403 #[test]
404 fn test_vmstate_macro_array_of_pointer_wrapped() {
405     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
406 
407     // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to
408     // VMSTATE_ARRAY_OF_POINTER)
409     assert_eq!(
410         unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
411         b"arr_ptr_wrap\0"
412     );
413     assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE);
414     assert_eq!(foo_fields[3].num_offset, 0);
415     assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 });
416     assert_eq!(foo_fields[3].version_id, 0);
417     assert_eq!(foo_fields[3].size, PTR_SIZE);
418     assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32);
419     assert_eq!(
420         foo_fields[3].flags.0,
421         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
422     );
423     assert!(foo_fields[3].vmsd.is_null());
424     assert!(foo_fields[3].field_exists.is_none());
425 
426     // The last VMStateField in VMSTATE_FOOC.
427     assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
428 }
429 
430 // =========================== Test VMSTATE_FOOD ===========================
431 // Test the use cases of the vmstate macro, corresponding to the following C
432 // macro variants:
433 //   * VMSTATE_FOOD:
434 //     - VMSTATE_VALIDATE
435 
436 // Add more member fields when vmstate_of/vmstate_struct support "test"
437 // parameter.
438 struct FooD;
439 
440 impl FooD {
441     fn validate_food_0(&self, _version_id: u8) -> bool {
442         true
443     }
444 
445     fn validate_food_1(_state: &FooD, _version_id: u8) -> bool {
446         false
447     }
448 }
449 
450 fn validate_food_2(_state: &FooD, _version_id: u8) -> bool {
451     true
452 }
453 
454 static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
455     name: c_str!("foo_d").as_ptr(),
456     version_id: 3,
457     minimum_version_id: 1,
458     fields: vmstate_fields! {
459         vmstate_validate!(FooD, c_str!("foo_d_0"), FooD::validate_food_0),
460         vmstate_validate!(FooD, c_str!("foo_d_1"), FooD::validate_food_1),
461         vmstate_validate!(FooD, c_str!("foo_d_2"), validate_food_2),
462     },
463     ..Zeroable::ZERO
464 };
465 
466 #[test]
467 fn test_vmstate_validate() {
468     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) };
469     let mut foo_d = FooD;
470     let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>();
471 
472     // 1st VMStateField in VMSTATE_FOOD
473     assert_eq!(
474         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
475         b"foo_d_0\0"
476     );
477     assert_eq!(foo_fields[0].offset, 0);
478     assert_eq!(foo_fields[0].num_offset, 0);
479     assert!(foo_fields[0].info.is_null());
480     assert_eq!(foo_fields[0].version_id, 0);
481     assert_eq!(foo_fields[0].size, 0);
482     assert_eq!(foo_fields[0].num, 0);
483     assert_eq!(
484         foo_fields[0].flags.0,
485         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0
486     );
487     assert!(foo_fields[0].vmsd.is_null());
488     assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) });
489 
490     // 2nd VMStateField in VMSTATE_FOOD
491     assert_eq!(
492         unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
493         b"foo_d_1\0"
494     );
495     assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) });
496 
497     // 3rd VMStateField in VMSTATE_FOOD
498     assert_eq!(
499         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
500         b"foo_d_2\0"
501     );
502     assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) });
503 
504     // The last VMStateField in VMSTATE_FOOD.
505     assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END);
506 }
507