qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] rust: add Derive macro unit tests
@ 2025-07-04 10:26 Manos Pitsidianakis
  2025-07-04 10:26 ` [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output Manos Pitsidianakis
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Manos Pitsidianakis @ 2025-07-04 10:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Alex Bennée, Paolo Bonzini, Zhao Liu,
	Manos Pitsidianakis

We don't currently test our proc macros and it'd be nice to do so.

Usually this would be done with something like
https://crates.io/crates/trybuild which runs cargo and tries to compile
a test input, and checks for success/failure. However we cannot use it
with meson directly, plus it would drag in a lot of dependencies anyway.

Instead of compiling, we can easily just expand test input into token
streams since we already split macro implementation into separate
functions, allowing us to either get a TokenStream back or a compile
error message.

You can run the added tests directly with this meson command:

  meson test rust-qemu-api-macros-tests

Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
Manos Pitsidianakis (2):
      rust/qemu-api-macros: normalize TryInto output
      rust/qemu-api-macros: add unit tests

 rust/qemu-api-macros/meson.build  |   3 +
 rust/qemu-api-macros/src/lib.rs   |   7 +-
 rust/qemu-api-macros/src/tests.rs | 135 ++++++++++++++++++++++++++++++++++++++
 rust/qemu-api-macros/src/utils.rs |   1 +
 4 files changed, 144 insertions(+), 2 deletions(-)
---
base-commit: c77283dd5d79149f4e7e9edd00f65416c648ee59
change-id: 20250704-rust_add_derive_macro_unit_tests-3e7daf905d62

--
γαῖα πυρί μιχθήτω



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output
  2025-07-04 10:26 [PATCH 0/2] rust: add Derive macro unit tests Manos Pitsidianakis
@ 2025-07-04 10:26 ` Manos Pitsidianakis
  2025-07-04 12:16   ` Zhao Liu
  2025-07-04 10:26 ` [PATCH 2/2] rust/qemu-api-macros: add unit tests Manos Pitsidianakis
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Manos Pitsidianakis @ 2025-07-04 10:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Alex Bennée, Paolo Bonzini, Zhao Liu,
	Manos Pitsidianakis

Remove extraneous `;` and add missing trailing comma to TryInto derive
macro to match rustfmt style. We will add a test in the followup commit
and we would like the inlined output in the test body to be properly
formatted as well.

No functional changes intended.

Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
 rust/qemu-api-macros/src/lib.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index c18bb4e036f4e7737f9b95ac300b7d1e8742ef1f..4b30bea9eafc7924bf593113c3f42c5b1010c4b9 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -201,7 +201,7 @@ fn derive_tryinto_body(
     let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
 
     Ok(quote! {
-        #(const #discriminants: #repr = #name::#discriminants as #repr;)*;
+        #(const #discriminants: #repr = #name::#discriminants as #repr;)*
         match value {
             #(#discriminants => core::result::Result::Ok(#name::#discriminants),)*
             _ => core::result::Result::Err(value),
@@ -229,7 +229,7 @@ pub const fn from_bits(value: #repr) -> Self {
                     #body
                 }) {
                     Ok(x) => x,
-                    Err(_) => panic!(#errmsg)
+                    Err(_) => panic!(#errmsg),
                 }
             }
         }

-- 
2.47.2



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] rust/qemu-api-macros: add unit tests
  2025-07-04 10:26 [PATCH 0/2] rust: add Derive macro unit tests Manos Pitsidianakis
  2025-07-04 10:26 ` [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output Manos Pitsidianakis
@ 2025-07-04 10:26 ` Manos Pitsidianakis
  2025-07-04 12:37   ` Zhao Liu
  2025-07-04 12:35 ` [PATCH 0/2] rust: add Derive macro " Zhao Liu
  2025-07-08  9:59 ` Paolo Bonzini
  3 siblings, 1 reply; 8+ messages in thread
From: Manos Pitsidianakis @ 2025-07-04 10:26 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-rust, Alex Bennée, Paolo Bonzini, Zhao Liu,
	Manos Pitsidianakis

Add unit tests to check Derive macro output for expected error messages,
or for expected correct codegen output.

Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
 rust/qemu-api-macros/meson.build  |   3 +
 rust/qemu-api-macros/src/lib.rs   |   3 +
 rust/qemu-api-macros/src/tests.rs | 135 ++++++++++++++++++++++++++++++++++++++
 rust/qemu-api-macros/src/utils.rs |   1 +
 4 files changed, 142 insertions(+)

diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build
index 8610ce1c8440c4b6e38a8462d4975bf76d72fb05..2152bcb99b30e4bdcc1c5b887b7903a37f6181c3 100644
--- a/rust/qemu-api-macros/meson.build
+++ b/rust/qemu-api-macros/meson.build
@@ -17,3 +17,6 @@ _qemu_api_macros_rs = rust.proc_macro(
 qemu_api_macros = declare_dependency(
   link_with: _qemu_api_macros_rs,
 )
+
+rust.test('rust-qemu-api-macros-tests', _qemu_api_macros_rs,
+          suite: ['unit', 'rust'])
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index 4b30bea9eafc7924bf593113c3f42c5b1010c4b9..6c6e9b683f047f79cb377e6d30e23490f66bd711 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -15,6 +15,9 @@
 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 0000000000000000000000000000000000000000..dfca7d4838f141783472a4e728312aebeb9b5a8b
--- /dev/null
+++ b/rust/qemu-api-macros/src/tests.rs
@@ -0,0 +1,135 @@
+// Copyright 2025, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use super::*;
+use quote::quote;
+
+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<proc_macro2::TokenStream, MacroError> =
+            $derive_fn;
+
+        let input: syn::DeriveInput = syn::parse2(input).unwrap();
+        let result = derive_fn(input);
+        let MacroError::Message(err, _) = result.unwrap_err() else {
+            panic!()
+        };
+        assert_eq!(err, error_msg);
+    }};
+}
+
+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<proc_macro2::TokenStream, MacroError> =
+            $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<<Foo as ::qemu_api::qom::ObjectImpl>::ParentType>
+            );
+            ::qemu_api::module_init! {
+                MODULE_INIT_QOM => unsafe {
+                    ::qemu_api::bindings::type_register_static(&<Foo as ::qemu_api::qom::ObjectImpl>::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<u8> for Foo {
+                type Error = u8;
+
+                #[allow(ambiguous_associated_items)]
+                fn try_from(value: u8) -> Result<Self, u8> {
+                    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),
+                    }
+                }
+            }
+        }
+    );
+}
diff --git a/rust/qemu-api-macros/src/utils.rs b/rust/qemu-api-macros/src/utils.rs
index 02c91aed7f6a6d33075bbaa8b4fec4536da94e60..6287d06090b60e7fc1da7fbd563d44effcc0039c 100644
--- a/rust/qemu-api-macros/src/utils.rs
+++ b/rust/qemu-api-macros/src/utils.rs
@@ -5,6 +5,7 @@
 use proc_macro2::Span;
 use quote::quote_spanned;
 
+#[derive(Debug)]
 pub enum MacroError {
     Message(String, Span),
     ParseError(syn::Error),

-- 
2.47.2



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output
  2025-07-04 10:26 ` [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output Manos Pitsidianakis
@ 2025-07-04 12:16   ` Zhao Liu
  0 siblings, 0 replies; 8+ messages in thread
From: Zhao Liu @ 2025-07-04 12:16 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, qemu-rust, Alex Bennée, Paolo Bonzini

On Fri, Jul 04, 2025 at 01:26:57PM +0300, Manos Pitsidianakis wrote:
> Date: Fri, 04 Jul 2025 13:26:57 +0300
> From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Subject: [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output
> X-Mailer: b4 0.14.2
> 
> Remove extraneous `;` and add missing trailing comma to TryInto derive
> macro to match rustfmt style. We will add a test in the followup commit
> and we would like the inlined output in the test body to be properly
> formatted as well.
> 
> No functional changes intended.
> 
> Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> ---
>  rust/qemu-api-macros/src/lib.rs | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] rust: add Derive macro unit tests
  2025-07-04 12:35 ` [PATCH 0/2] rust: add Derive macro " Zhao Liu
@ 2025-07-04 12:18   ` Manos Pitsidianakis
  0 siblings, 0 replies; 8+ messages in thread
From: Manos Pitsidianakis @ 2025-07-04 12:18 UTC (permalink / raw)
  To: Zhao Liu; +Cc: qemu-devel, qemu-rust, Alex Bennée, Paolo Bonzini

On Fri, Jul 4, 2025 at 3:14 PM Zhao Liu <zhao1.liu@intel.com> wrote:
>
> > You can run the added tests directly with this meson command:
> >
> >   meson test rust-qemu-api-macros-tests
>
> Hi Manos, do you meet this error:
>
> Traceback (most recent call last):
>   File "/usr/lib/python3/dist-packages/mesonbuild/mesonmain.py", line 146, in run
>     return options.run_func(options)
>   File "/usr/lib/python3/dist-packages/mesonbuild/mtest.py", line 2004, in run
>     b = build.load(options.wd)
>   File "/usr/lib/python3/dist-packages/mesonbuild/build.py", line 2868, in load
>     obj = pickle.load(f)
> ModuleNotFoundError: No module named 'mesonbuild.options'
>
> ERROR: Unhandled python exception
>
>     This is a Meson bug and should be reported!
> ---
>
> But I've tested this series via `make check`.
>
>
>
>

Sounds like it used your global meson installation instead of the one
from the virtual environment of your build directory.

Thank you for testing!

-- 
Manos Pitsidianakis
Emulation and Virtualization Engineer at Linaro Ltd


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] rust: add Derive macro unit tests
  2025-07-04 10:26 [PATCH 0/2] rust: add Derive macro unit tests Manos Pitsidianakis
  2025-07-04 10:26 ` [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output Manos Pitsidianakis
  2025-07-04 10:26 ` [PATCH 2/2] rust/qemu-api-macros: add unit tests Manos Pitsidianakis
@ 2025-07-04 12:35 ` Zhao Liu
  2025-07-04 12:18   ` Manos Pitsidianakis
  2025-07-08  9:59 ` Paolo Bonzini
  3 siblings, 1 reply; 8+ messages in thread
From: Zhao Liu @ 2025-07-04 12:35 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, qemu-rust, Alex Bennée, Paolo Bonzini

> You can run the added tests directly with this meson command:
> 
>   meson test rust-qemu-api-macros-tests

Hi Manos, do you meet this error:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/mesonbuild/mesonmain.py", line 146, in run
    return options.run_func(options)
  File "/usr/lib/python3/dist-packages/mesonbuild/mtest.py", line 2004, in run
    b = build.load(options.wd)
  File "/usr/lib/python3/dist-packages/mesonbuild/build.py", line 2868, in load
    obj = pickle.load(f)
ModuleNotFoundError: No module named 'mesonbuild.options'

ERROR: Unhandled python exception

    This is a Meson bug and should be reported!
---

But I've tested this series via `make check`.






^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] rust/qemu-api-macros: add unit tests
  2025-07-04 10:26 ` [PATCH 2/2] rust/qemu-api-macros: add unit tests Manos Pitsidianakis
@ 2025-07-04 12:37   ` Zhao Liu
  0 siblings, 0 replies; 8+ messages in thread
From: Zhao Liu @ 2025-07-04 12:37 UTC (permalink / raw)
  To: Manos Pitsidianakis
  Cc: qemu-devel, qemu-rust, Alex Bennée, Paolo Bonzini

On Fri, Jul 04, 2025 at 01:26:58PM +0300, Manos Pitsidianakis wrote:
> Date: Fri, 04 Jul 2025 13:26:58 +0300
> From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Subject: [PATCH 2/2] rust/qemu-api-macros: add unit tests
> X-Mailer: b4 0.14.2
> 
> Add unit tests to check Derive macro output for expected error messages,
> or for expected correct codegen output.
> 
> Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> ---
>  rust/qemu-api-macros/meson.build  |   3 +
>  rust/qemu-api-macros/src/lib.rs   |   3 +
>  rust/qemu-api-macros/src/tests.rs | 135 ++++++++++++++++++++++++++++++++++++++
>  rust/qemu-api-macros/src/utils.rs |   1 +
>  4 files changed, 142 insertions(+)
> 
> diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build
> index 8610ce1c8440c4b6e38a8462d4975bf76d72fb05..2152bcb99b30e4bdcc1c5b887b7903a37f6181c3 100644
> --- a/rust/qemu-api-macros/meson.build
> +++ b/rust/qemu-api-macros/meson.build
> @@ -17,3 +17,6 @@ _qemu_api_macros_rs = rust.proc_macro(
>  qemu_api_macros = declare_dependency(
>    link_with: _qemu_api_macros_rs,
>  )
> +
> +rust.test('rust-qemu-api-macros-tests', _qemu_api_macros_rs,
> +          suite: ['unit', 'rust'])
> diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
> index 4b30bea9eafc7924bf593113c3f42c5b1010c4b9..6c6e9b683f047f79cb377e6d30e23490f66bd711 100644
> --- a/rust/qemu-api-macros/src/lib.rs
> +++ b/rust/qemu-api-macros/src/lib.rs
> @@ -15,6 +15,9 @@
>  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 0000000000000000000000000000000000000000..dfca7d4838f141783472a4e728312aebeb9b5a8b
> --- /dev/null
> +++ b/rust/qemu-api-macros/src/tests.rs
> @@ -0,0 +1,135 @@
> +// Copyright 2025, Linaro Limited
> +// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +use super::*;
> +use quote::quote;
> +
> +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<proc_macro2::TokenStream, MacroError> =
> +            $derive_fn;
> +
> +        let input: syn::DeriveInput = syn::parse2(input).unwrap();
> +        let result = derive_fn(input);
> +        let MacroError::Message(err, _) = result.unwrap_err() else {
> +            panic!()
> +        };
> +        assert_eq!(err, error_msg);
> +    }};
> +}
> +
> +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<proc_macro2::TokenStream, MacroError> =
> +            $derive_fn;
> +
> +        let input: syn::DeriveInput = syn::parse2(input).unwrap();
> +        let result = derive_fn(input).unwrap();
> +        assert_eq!(result.to_string(), expected.to_string());
> +    }};
> +}

Good examples to test macros. LGTM,

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] rust: add Derive macro unit tests
  2025-07-04 10:26 [PATCH 0/2] rust: add Derive macro unit tests Manos Pitsidianakis
                   ` (2 preceding siblings ...)
  2025-07-04 12:35 ` [PATCH 0/2] rust: add Derive macro " Zhao Liu
@ 2025-07-08  9:59 ` Paolo Bonzini
  3 siblings, 0 replies; 8+ messages in thread
From: Paolo Bonzini @ 2025-07-08  9:59 UTC (permalink / raw)
  To: Manos Pitsidianakis, qemu-devel; +Cc: qemu-rust, Alex Bennée, Zhao Liu

On 7/4/25 12:26, Manos Pitsidianakis wrote:
> We don't currently test our proc macros and it'd be nice to do so.
> 
> Usually this would be done with something like
> https://crates.io/crates/trybuild which runs cargo and tries to compile
> a test input, and checks for success/failure. However we cannot use it
> with meson directly, plus it would drag in a lot of dependencies anyway.
> 
> Instead of compiling, we can easily just expand test input into token
> streams since we already split macro implementation into separate
> functions, allowing us to either get a TokenStream back or a compile
> error message.
> 
> You can run the added tests directly with this meson command:
> 
>    meson test rust-qemu-api-macros-tests
> 
> Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>

Nice.  Adjusted for MacroError removal and applied, thanks.

Paolo



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-07-08 21:32 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-04 10:26 [PATCH 0/2] rust: add Derive macro unit tests Manos Pitsidianakis
2025-07-04 10:26 ` [PATCH 1/2] rust/qemu-api-macros: normalize TryInto output Manos Pitsidianakis
2025-07-04 12:16   ` Zhao Liu
2025-07-04 10:26 ` [PATCH 2/2] rust/qemu-api-macros: add unit tests Manos Pitsidianakis
2025-07-04 12:37   ` Zhao Liu
2025-07-04 12:35 ` [PATCH 0/2] rust: add Derive macro " Zhao Liu
2025-07-04 12:18   ` Manos Pitsidianakis
2025-07-08  9:59 ` Paolo Bonzini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).