From a721d9a9f3dd7bb9d6ed81ea1a11a1157755741c Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 4 Jul 2025 13:26:58 +0300 Subject: rust/qemu-api-macros: add unit tests Add unit tests to check Derive macro output for expected error messages, or for expected correct codegen output. Signed-off-by: Manos Pitsidianakis Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250704-rust_add_derive_macro_unit_tests-v1-2-ebd47fa7f78f@linaro.org [Remove usage of MacroError. - Paolo] Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 3 + rust/qemu-api-macros/src/tests.rs | 137 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 rust/qemu-api-macros/src/tests.rs (limited to 'rust/qemu-api-macros/src') diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 5bbf8c6..b525d89 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -11,6 +11,9 @@ use syn::{ mod bits; use bits::BitsConstInternal; +#[cfg(test)] +mod tests; + fn get_fields<'a>( input: &'a DeriveInput, msg: &str, diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs new file mode 100644 index 0000000..d6dcd62 --- /dev/null +++ b/rust/qemu-api-macros/src/tests.rs @@ -0,0 +1,137 @@ +// Copyright 2025, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use quote::quote; + +use super::*; + +macro_rules! derive_compile_fail { + ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ + let input: proc_macro2::TokenStream = $input; + let error_msg: &str = $error_msg; + let derive_fn: fn(input: syn::DeriveInput) -> Result = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input); + let err = result.unwrap_err().into_compile_error(); + assert_eq!( + err.to_string(), + quote! { ::core::compile_error! { #error_msg } }.to_string() + ); + }}; +} + +macro_rules! derive_compile { + ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ + let input: proc_macro2::TokenStream = $input; + let expected: proc_macro2::TokenStream = $($expected)*; + let derive_fn: fn(input: syn::DeriveInput) -> Result = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input).unwrap(); + assert_eq!(result.to_string(), expected.to_string()); + }}; +} + +#[test] +fn test_derive_object() { + derive_compile_fail!( + derive_object_or_error, + quote! { + #[derive(Object)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(C)] required for #[derive(Object)]" + ); + derive_compile!( + derive_object_or_error, + quote! { + #[derive(Object)] + #[repr(C)] + struct Foo { + _unused: [u8; 0], + } + }, + quote! { + ::qemu_api::assert_field_type!( + Foo, + _unused, + ::qemu_api::qom::ParentField<::ParentType> + ); + ::qemu_api::module_init! { + MODULE_INIT_QOM => unsafe { + ::qemu_api::bindings::type_register_static(&::TYPE_INFO); + } + } + } + ); +} + +#[test] +fn test_derive_tryinto() { + derive_compile_fail!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" + ); + derive_compile!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + #[repr(u8)] + enum Foo { + First = 0, + Second, + } + }, + quote! { + impl Foo { + #[allow(dead_code)] + pub const fn into_bits(self) -> u8 { + self as u8 + } + + #[allow(dead_code)] + pub const fn from_bits(value: u8) -> Self { + match ({ + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + }) { + Ok(x) => x, + Err(_) => panic!("invalid value for Foo"), + } + } + } + + impl core::convert::TryFrom for Foo { + type Error = u8; + + #[allow(ambiguous_associated_items)] + fn try_from(value: u8) -> Result { + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + } + } + } + ); +} -- cgit v1.1