aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api-macros/src/tests.rs
blob: d6dcd62fcf680591b1181f2280b43f894e46c2b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2025, Linaro Limited
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// 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<proc_macro2::TokenStream, syn::Error> =
            $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<proc_macro2::TokenStream, syn::Error> =
            $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),
                    }
                }
            }
        }
    );
}