xref: /openbmc/qemu/rust/qemu-api-macros/src/tests.rs (revision b92b39af4219df4250f121f64d215506909c7404)
1 // Copyright 2025, Linaro Limited
2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
3 // SPDX-License-Identifier: GPL-2.0-or-later
4 
5 use quote::quote;
6 
7 use super::*;
8 
9 macro_rules! derive_compile_fail {
10     ($derive_fn:ident, $input:expr, $error_msg:expr) => {{
11         let input: proc_macro2::TokenStream = $input;
12         let error_msg: &str = $error_msg;
13         let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> =
14             $derive_fn;
15 
16         let input: syn::DeriveInput = syn::parse2(input).unwrap();
17         let result = derive_fn(input);
18         let err = result.unwrap_err().into_compile_error();
19         assert_eq!(
20             err.to_string(),
21             quote! { ::core::compile_error! { #error_msg } }.to_string()
22         );
23     }};
24 }
25 
26 macro_rules! derive_compile {
27     ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{
28         let input: proc_macro2::TokenStream = $input;
29         let expected: proc_macro2::TokenStream = $($expected)*;
30         let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> =
31             $derive_fn;
32 
33         let input: syn::DeriveInput = syn::parse2(input).unwrap();
34         let result = derive_fn(input).unwrap();
35         assert_eq!(result.to_string(), expected.to_string());
36     }};
37 }
38 
39 #[test]
test_derive_object()40 fn test_derive_object() {
41     derive_compile_fail!(
42         derive_object_or_error,
43         quote! {
44             #[derive(Object)]
45             struct Foo {
46                 _unused: [u8; 0],
47             }
48         },
49         "#[repr(C)] required for #[derive(Object)]"
50     );
51     derive_compile!(
52         derive_object_or_error,
53         quote! {
54             #[derive(Object)]
55             #[repr(C)]
56             struct Foo {
57                 _unused: [u8; 0],
58             }
59         },
60         quote! {
61             ::qemu_api::assert_field_type!(
62                 Foo,
63                 _unused,
64                 ::qemu_api::qom::ParentField<<Foo as ::qemu_api::qom::ObjectImpl>::ParentType>
65             );
66             ::qemu_api::module_init! {
67                 MODULE_INIT_QOM => unsafe {
68                     ::qemu_api::bindings::type_register_static(&<Foo as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
69                 }
70             }
71         }
72     );
73 }
74 
75 #[test]
test_derive_tryinto()76 fn test_derive_tryinto() {
77     derive_compile_fail!(
78         derive_tryinto_or_error,
79         quote! {
80             #[derive(TryInto)]
81             struct Foo {
82                 _unused: [u8; 0],
83             }
84         },
85         "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]"
86     );
87     derive_compile!(
88         derive_tryinto_or_error,
89         quote! {
90             #[derive(TryInto)]
91             #[repr(u8)]
92             enum Foo {
93                 First = 0,
94                 Second,
95             }
96         },
97         quote! {
98             impl Foo {
99                 #[allow(dead_code)]
100                 pub const fn into_bits(self) -> u8 {
101                     self as u8
102                 }
103 
104                 #[allow(dead_code)]
105                 pub const fn from_bits(value: u8) -> Self {
106                     match ({
107                         const First: u8 = Foo::First as u8;
108                         const Second: u8 = Foo::Second as u8;
109                         match value {
110                             First => core::result::Result::Ok(Foo::First),
111                             Second => core::result::Result::Ok(Foo::Second),
112                             _ => core::result::Result::Err(value),
113                         }
114                     }) {
115                         Ok(x) => x,
116                         Err(_) => panic!("invalid value for Foo"),
117                     }
118                 }
119             }
120 
121             impl core::convert::TryFrom<u8> for Foo {
122                 type Error = u8;
123 
124                 #[allow(ambiguous_associated_items)]
125                 fn try_from(value: u8) -> Result<Self, u8> {
126                     const First: u8 = Foo::First as u8;
127                     const Second: u8 = Foo::Second as u8;
128                     match value {
129                         First => core::result::Result::Ok(Foo::First),
130                         Second => core::result::Result::Ok(Foo::Second),
131                         _ => core::result::Result::Err(value),
132                     }
133                 }
134             }
135         }
136     );
137 }
138