use proc_macro2::TokenStream;
use quote::quote;
fn fmt(tokens: &TokenStream) -> String {
match syn::parse_file(&tokens.to_string()) {
Ok(file) => prettyplease::unparse(&file),
Err(_) => format!("{tokens}\n"),
}
}
fn snapshot_derive(input: TokenStream) -> String {
let output = consortium_tee_macros_impl::derive_tee_param(input.clone());
format!(
"// Input:\n#[derive(consortium_tee::TeeParam)]\n{}\n// Expands to:\n{}",
fmt(&input),
fmt(&output)
)
}
fn snapshot_service(input: TokenStream) -> String {
let output = consortium_tee_macros_impl::tee_service(input.clone());
let input_code = quote! {
consortium_tee::tee_service! {
#input
}
};
format!(
"// Input:\n{}\n// Expands to:\n{}",
fmt(&input_code),
fmt(&output)
)
}
fn snapshot_command(attr: TokenStream, item: TokenStream) -> String {
let output = consortium_tee_macros_impl::tee_command(attr.clone(), item.clone());
let attr_line = if attr.is_empty() {
"#[consortium_tee::tee_command]\n".to_string()
} else {
format!("#[consortium_tee::tee_command({attr})]\n")
};
format!(
"// Input:\n{}{}\n// Expands to:\n{}",
attr_line,
fmt(&item),
fmt(&output)
)
}
#[test]
fn tee_service_expansion() {
insta::assert_snapshot!(snapshot_service(quote! {
context ServiceContext
commands { ping, echo_bytes }
}));
}
#[test]
fn tee_service_expansion_no_ctx() {
insta::assert_snapshot!(snapshot_service(quote! {
commands { ping, echo_bytes, hello }
}));
}
#[test]
fn tee_service_fail_unknown_conflict() {
insta::assert_snapshot!(snapshot_service(quote! {
commands { hello, world, unknown, conflict }
}));
}
#[test]
fn tee_service_live_test() {
insta::assert_snapshot!(snapshot_service(quote! {
commands { hello_world }
}));
}
#[test]
fn tee_command_live_test() {
insta::assert_snapshot!(snapshot_command(
quote! {},
quote! {
fn hello_world(a: u32) -> u32 {
a
}
}
))
}
#[test]
fn tee_command_expansion() {
insta::assert_snapshot!(snapshot_command(
quote! { codec = TestCodec, ctx },
quote! {
fn echo(
ctx: &mut ServiceContext,
input: &[u8],
counter: &mut u64,
) -> ::consortium_tee::TeeResult<Vec<u8>> {
*counter += 1;
Ok(input.to_vec())
}
},
));
}
#[test]
fn tee_command_context_by_name_warning() {
insta::assert_snapshot!(snapshot_command(
quote! { ctx = context },
quote! {
fn ping(value: u32, context: &mut ServiceContext) -> u32 {
value + 1
}
},
));
}
#[test]
fn tee_command_context_by_index_warning() {
insta::assert_snapshot!(snapshot_command(
quote! { ctx = 0 },
quote! {
fn ping(context: &mut ServiceContext, value: u32) -> u32 {
value + 1
}
},
));
}
#[test]
fn tee_command_ctx_missing_parameter_error() {
insta::assert_snapshot!(snapshot_command(
quote! { ctx },
quote! {
fn ping() {}
},
));
}
#[test]
fn tee_command_ctx_non_reference_error() {
insta::assert_snapshot!(snapshot_command(
quote! { ctx },
quote! {
fn ping(value: u32) -> u32 {
value
}
},
));
}
#[test]
fn tee_command_mixed_primitive_and_serialized_parameters() {
insta::assert_snapshot!(snapshot_command(
quote! { codec = TestCodec },
quote! {
fn configure(slot: u32, config: DeviceConfig) -> u32 {
slot + config.id as u32
}
},
));
}
#[test]
fn tee_command_typed_memref_inout_parameter() {
insta::assert_snapshot!(snapshot_command(
quote! { codec = TestCodec },
quote! {
fn adjust_config(config: &mut DeviceConfig) {
config.limit += 1;
}
},
));
}
#[test]
fn tee_command_primitive_return() {
insta::assert_snapshot!(snapshot_command(
quote! {},
quote! {
fn read_counter() -> u64 {
0x1122_3344_5566_7788
}
},
));
}
#[test]
fn tee_command_serialized_return_with_explicit_codec() {
insta::assert_snapshot!(snapshot_command(
quote! { codec = TestCodec },
quote! {
fn read_config(slot: u32) -> DeviceConfig {
DeviceConfig {
id: slot as u8,
limit: 1024,
}
}
},
));
}
#[test]
fn tee_param_derive_expansion() {
insta::assert_snapshot!(snapshot_derive(quote! {
struct CommandPayload<T> {
opcode: u32,
body: T,
}
}));
}
#[test]
fn tee_param_max_size_override() {
insta::assert_snapshot!(snapshot_derive(quote! {
#[tee(max_size = 4096)]
struct LargePayload {
body: u32,
}
}));
}
#[test]
fn tee_param_empty_attribute_defaults_max_size() {
insta::assert_snapshot!(snapshot_derive(quote! {
#[tee]
struct DefaultPayload {
body: u32,
}
}));
}
#[test]
fn tee_param_invalid_attribute_error() {
insta::assert_snapshot!(snapshot_derive(quote! {
#[tee(buffer = 4096)]
struct BadAttribute {
body: Vec<u8>,
}
}));
}
#[test]
fn tee_param_small_integer_fields_are_serialized_without_warnings() {
insta::assert_snapshot!(snapshot_derive(quote! {
enum SmallFieldPayload {
Small {
a: u8,
b: u16,
c: i8,
d: i16,
e: char,
},
Nested(Option<u8>),
}
}));
}
#[test]
fn tee_param_rejection_errors() {
insta::assert_snapshot!(snapshot_derive(quote! {
struct RejectedPayload<'a> {
raw: *const u8,
borrowed: &'a u32,
callback: fn(u32) -> u32,
platform_width: Option<usize>,
float_value: f32,
too_wide: u128,
erased: dyn Send,
}
}));
}
#[test]
fn tee_command_too_many_parameters_error() {
insta::assert_snapshot!(snapshot_command(
quote! {},
quote! {
fn crowded(a: u32, b: u32, c: u32, d: u32, e: u32) {}
},
));
}
#[test]
fn tee_command_unsupported_type_error() {
insta::assert_snapshot!(snapshot_command(
quote! {},
quote! {
fn unsupported(input: (u32, u64)) -> (u32, u64) {
input
}
},
));
}