Skip to content

Commit 8df1b00

Browse files
trueptolemybonzini
authored andcommitted
rust/vmstate: Add unit test for pointer case
Add a unit test to cover some patterns accepted by vmstate_of macro, which correspond to the following C version macros: * VMSTATE_POINTER * VMSTATE_ARRAY_OF_POINTER Note: Currently, vmstate_struct can't handle the pointer to structure case. Leave this case as a FIXME and use vmstate_unused as a place holder. Signed-off-by: Zhao Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 57c327f commit 8df1b00

File tree

1 file changed

+115
-4
lines changed

1 file changed

+115
-4
lines changed

rust/qemu-api/tests/vmstate_tests.rs

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
// Author(s): Zhao Liu <[email protected]>
33
// SPDX-License-Identifier: GPL-2.0-or-later
44

5-
use std::{ffi::CStr, mem::size_of, slice};
5+
use std::{ffi::CStr, mem::size_of, ptr::NonNull, slice};
66

77
use qemu_api::{
88
bindings::{
9-
vmstate_info_bool, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64,
10-
vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
9+
vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
10+
vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
1111
},
1212
c_str,
13-
cell::BqlCell,
13+
cell::{BqlCell, Opaque},
14+
impl_vmstate_forward,
1415
vmstate::{VMStateDescription, VMStateField},
1516
vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused,
1617
zeroable::Zeroable,
@@ -286,3 +287,113 @@ fn test_vmstate_macro_array() {
286287
// The last VMStateField in VMSTATE_FOOB.
287288
assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END);
288289
}
290+
291+
// =========================== Test VMSTATE_FOOC ===========================
292+
// Test the use cases of the vmstate macro, corresponding to the following C
293+
// macro variants:
294+
// * VMSTATE_FOOC:
295+
// - VMSTATE_POINTER
296+
// - VMSTATE_ARRAY_OF_POINTER
297+
struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible.
298+
299+
impl_vmstate_forward!(FooCWrapper);
300+
301+
#[repr(C)]
302+
#[derive(qemu_api_macros::offsets)]
303+
struct FooC {
304+
ptr: *const i32,
305+
ptr_a: NonNull<FooA>,
306+
arr_ptr: [Box<u8>; FOO_ARRAY_MAX],
307+
arr_ptr_wrap: FooCWrapper,
308+
}
309+
310+
static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
311+
name: c_str!("foo_c").as_ptr(),
312+
version_id: 3,
313+
minimum_version_id: 1,
314+
fields: vmstate_fields! {
315+
vmstate_of!(FooC, ptr).with_version_id(2),
316+
// FIXME: Currently vmstate_struct doesn't support the pointer to structure.
317+
// VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
318+
vmstate_unused!(size_of::<NonNull<FooA>>()),
319+
vmstate_of!(FooC, arr_ptr),
320+
vmstate_of!(FooC, arr_ptr_wrap),
321+
},
322+
..Zeroable::ZERO
323+
};
324+
325+
const PTR_SIZE: usize = size_of::<*mut ()>();
326+
327+
#[test]
328+
fn test_vmstate_pointer() {
329+
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
330+
331+
// 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER)
332+
assert_eq!(
333+
unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
334+
b"ptr\0"
335+
);
336+
assert_eq!(foo_fields[0].offset, 0);
337+
assert_eq!(foo_fields[0].num_offset, 0);
338+
assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 });
339+
assert_eq!(foo_fields[0].version_id, 2);
340+
assert_eq!(foo_fields[0].size, 4);
341+
assert_eq!(foo_fields[0].num, 0);
342+
assert_eq!(
343+
foo_fields[0].flags.0,
344+
VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0
345+
);
346+
assert!(foo_fields[0].vmsd.is_null());
347+
assert!(foo_fields[0].field_exists.is_none());
348+
}
349+
350+
#[test]
351+
fn test_vmstate_macro_array_of_pointer() {
352+
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
353+
354+
// 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to
355+
// VMSTATE_ARRAY_OF_POINTER)
356+
assert_eq!(
357+
unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
358+
b"arr_ptr\0"
359+
);
360+
assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE);
361+
assert_eq!(foo_fields[2].num_offset, 0);
362+
assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
363+
assert_eq!(foo_fields[2].version_id, 0);
364+
assert_eq!(foo_fields[2].size, PTR_SIZE);
365+
assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32);
366+
assert_eq!(
367+
foo_fields[2].flags.0,
368+
VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
369+
);
370+
assert!(foo_fields[2].vmsd.is_null());
371+
assert!(foo_fields[2].field_exists.is_none());
372+
}
373+
374+
#[test]
375+
fn test_vmstate_macro_array_of_pointer_wrapped() {
376+
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
377+
378+
// 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to
379+
// VMSTATE_ARRAY_OF_POINTER)
380+
assert_eq!(
381+
unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
382+
b"arr_ptr_wrap\0"
383+
);
384+
assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE);
385+
assert_eq!(foo_fields[3].num_offset, 0);
386+
assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
387+
assert_eq!(foo_fields[3].version_id, 0);
388+
assert_eq!(foo_fields[3].size, PTR_SIZE);
389+
assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32);
390+
assert_eq!(
391+
foo_fields[2].flags.0,
392+
VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
393+
);
394+
assert!(foo_fields[3].vmsd.is_null());
395+
assert!(foo_fields[3].field_exists.is_none());
396+
397+
// The last VMStateField in VMSTATE_FOOC.
398+
assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
399+
}

0 commit comments

Comments
 (0)