Skip to content

Commit 1998502

Browse files
trueptolemybonzini
authored andcommitted
rust/vmstate: Add unit test for vmstate_of macro
The vmstate has too many combinations of VMStateFlags and VMStateField. Currently, the best way to test is to ensure that the Rust vmstate definition is consistent with the (possibly corresponding) C version. Add a unit test to cover some patterns accepted by vmstate_of macro, which correspond to the following C version macros: * VMSTATE_U16 * VMSTATE_UNUSED * VMSTATE_VARRAY_UINT16_UNSAFE * VMSTATE_VARRAY_MULTIPLY Note: Because vmstate_info_* are defined in vmstate-types.c, it's necessary to link libmigration to rust unit tests. In the future, maybe it's possible to spilt libmigration from rust_qemu_api_objs. Signed-off-by: Zhao Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Bonzini <[email protected]>
1 parent b131003 commit 1998502

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

rust/qemu-api/meson.build

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ rust_qemu_api_objs = static_library(
5858
libchardev.extract_all_objects(recursive: false),
5959
libcrypto.extract_all_objects(recursive: false),
6060
libauthz.extract_all_objects(recursive: false),
61-
libio.extract_all_objects(recursive: false)])
61+
libio.extract_all_objects(recursive: false),
62+
libmigration.extract_all_objects(recursive: false)])
6263
rust_qemu_api_deps = declare_dependency(
6364
dependencies: [
6465
qom_ss.dependencies(),
@@ -71,7 +72,7 @@ rust_qemu_api_deps = declare_dependency(
7172
test('rust-qemu-api-integration',
7273
executable(
7374
'rust-qemu-api-integration',
74-
'tests/tests.rs',
75+
files('tests/tests.rs', 'tests/vmstate_tests.rs'),
7576
override_options: ['rust_std=2021', 'build.rust_std=2021'],
7677
rust_args: ['--test'],
7778
install: false,

rust/qemu-api/tests/tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use qemu_api::{
1717
zeroable::Zeroable,
1818
};
1919

20+
mod vmstate_tests;
21+
2022
// Test that macros can compile.
2123
pub static VMSTATE: VMStateDescription = VMStateDescription {
2224
name: c_str!("name").as_ptr(),

rust/qemu-api/tests/vmstate_tests.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (C) 2025 Intel Corporation.
2+
// Author(s): Zhao Liu <[email protected]>
3+
// SPDX-License-Identifier: GPL-2.0-or-later
4+
5+
use std::{ffi::CStr, mem::size_of, slice};
6+
7+
use qemu_api::{
8+
bindings::{vmstate_info_int8, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags},
9+
c_str,
10+
vmstate::{VMStateDescription, VMStateField},
11+
vmstate_fields, vmstate_of, vmstate_unused,
12+
zeroable::Zeroable,
13+
};
14+
15+
const FOO_ARRAY_MAX: usize = 3;
16+
17+
// =========================== Test VMSTATE_FOOA ===========================
18+
// Test the use cases of the vmstate macro, corresponding to the following C
19+
// macro variants:
20+
// * VMSTATE_FOOA:
21+
// - VMSTATE_U16
22+
// - VMSTATE_UNUSED
23+
// - VMSTATE_VARRAY_UINT16_UNSAFE
24+
// - VMSTATE_VARRAY_MULTIPLY
25+
#[repr(C)]
26+
#[derive(qemu_api_macros::offsets)]
27+
struct FooA {
28+
arr: [u8; FOO_ARRAY_MAX],
29+
num: u16,
30+
arr_mul: [i8; FOO_ARRAY_MAX],
31+
num_mul: u32,
32+
elem: i8,
33+
}
34+
35+
static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
36+
name: c_str!("foo_a").as_ptr(),
37+
version_id: 1,
38+
minimum_version_id: 1,
39+
fields: vmstate_fields! {
40+
vmstate_of!(FooA, elem),
41+
vmstate_unused!(size_of::<i64>()),
42+
vmstate_of!(FooA, arr[0 .. num]).with_version_id(0),
43+
vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]),
44+
},
45+
..Zeroable::ZERO
46+
};
47+
48+
#[test]
49+
fn test_vmstate_uint16() {
50+
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
51+
52+
// 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16)
53+
assert_eq!(
54+
unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
55+
b"elem\0"
56+
);
57+
assert_eq!(foo_fields[0].offset, 16);
58+
assert_eq!(foo_fields[0].num_offset, 0);
59+
assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 });
60+
assert_eq!(foo_fields[0].version_id, 0);
61+
assert_eq!(foo_fields[0].size, 1);
62+
assert_eq!(foo_fields[0].num, 0);
63+
assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE);
64+
assert!(foo_fields[0].vmsd.is_null());
65+
assert!(foo_fields[0].field_exists.is_none());
66+
}
67+
68+
#[test]
69+
fn test_vmstate_unused() {
70+
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
71+
72+
// 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED)
73+
assert_eq!(
74+
unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
75+
b"unused\0"
76+
);
77+
assert_eq!(foo_fields[1].offset, 0);
78+
assert_eq!(foo_fields[1].num_offset, 0);
79+
assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer });
80+
assert_eq!(foo_fields[1].version_id, 0);
81+
assert_eq!(foo_fields[1].size, 8);
82+
assert_eq!(foo_fields[1].num, 0);
83+
assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER);
84+
assert!(foo_fields[1].vmsd.is_null());
85+
assert!(foo_fields[1].field_exists.is_none());
86+
}
87+
88+
#[test]
89+
fn test_vmstate_varray_uint16_unsafe() {
90+
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
91+
92+
// 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to
93+
// VMSTATE_VARRAY_UINT16_UNSAFE)
94+
assert_eq!(
95+
unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
96+
b"arr\0"
97+
);
98+
assert_eq!(foo_fields[2].offset, 0);
99+
assert_eq!(foo_fields[2].num_offset, 4);
100+
assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
101+
assert_eq!(foo_fields[2].version_id, 0);
102+
assert_eq!(foo_fields[2].size, 1);
103+
assert_eq!(foo_fields[2].num, 0);
104+
assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16);
105+
assert!(foo_fields[2].vmsd.is_null());
106+
assert!(foo_fields[2].field_exists.is_none());
107+
}
108+
109+
#[test]
110+
fn test_vmstate_varray_multiply() {
111+
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
112+
113+
// 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to
114+
// VMSTATE_VARRAY_MULTIPLY)
115+
assert_eq!(
116+
unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
117+
b"arr_mul\0"
118+
);
119+
assert_eq!(foo_fields[3].offset, 6);
120+
assert_eq!(foo_fields[3].num_offset, 12);
121+
assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 });
122+
assert_eq!(foo_fields[3].version_id, 0);
123+
assert_eq!(foo_fields[3].size, 1);
124+
assert_eq!(foo_fields[3].num, 16);
125+
assert_eq!(
126+
foo_fields[3].flags.0,
127+
VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
128+
);
129+
assert!(foo_fields[3].vmsd.is_null());
130+
assert!(foo_fields[3].field_exists.is_none());
131+
132+
// The last VMStateField in VMSTATE_FOOA.
133+
assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
134+
}

0 commit comments

Comments
 (0)