From 5702da4df39026f7aa7647a5932fa523ff205bf1 Mon Sep 17 00:00:00 2001 From: Tyler Southwick Date: Fri, 11 Oct 2024 22:58:37 -0700 Subject: [PATCH] adding tests for macro and enhance macro This adds support for the `machine` macro to support async functions, no arg functions, and multi arg functions. --- Cargo.lock | 121 ++++++++++++++++++ macros/Cargo.toml | 5 + macros/src/lib.rs | 77 ++++++++--- .../tests/macros/failures/invalid_return.rs | 6 + .../macros/failures/invalid_return.stderr | 7 + .../tests/macros/failures/unsafe_function.rs | 4 + .../macros/failures/unsafe_function.stderr | 7 + macros/tests/macros/success/async_call.rs | 15 +++ macros/tests/macros/success/empty_function.rs | 15 +++ macros/tests/macros/success/multiple_calls.rs | 15 +++ macros/tests/macros/success/simple_call.rs | 15 +++ 11 files changed, 267 insertions(+), 20 deletions(-) create mode 100644 macros/tests/macros/failures/invalid_return.rs create mode 100644 macros/tests/macros/failures/invalid_return.stderr create mode 100644 macros/tests/macros/failures/unsafe_function.rs create mode 100644 macros/tests/macros/failures/unsafe_function.stderr create mode 100644 macros/tests/macros/success/async_call.rs create mode 100644 macros/tests/macros/success/empty_function.rs create mode 100644 macros/tests/macros/success/multiple_calls.rs create mode 100644 macros/tests/macros/success/simple_call.rs diff --git a/Cargo.lock b/Cargo.lock index ab1a5550..a41507b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,6 +358,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.2.8" @@ -531,6 +537,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "gloo-timers" version = "0.2.4" @@ -543,6 +555,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heck" version = "0.3.3" @@ -625,6 +643,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -688,6 +716,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "js-sys" version = "0.3.60" @@ -877,9 +911,12 @@ dependencies = [ name = "netsim-embed-macros" version = "0.2.0" dependencies = [ + "async-std", + "netsim-embed", "quote", "rand", "syn 2.0.48", + "trybuild", ] [[package]] @@ -1102,6 +1139,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "serde" version = "1.0.195" @@ -1122,6 +1165,27 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "signal-hook" version = "0.3.14" @@ -1278,6 +1342,54 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "trybuild" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + [[package]] name = "udp-socket" version = "0.1.5" @@ -1664,3 +1776,12 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 8b69d92f..3dc2d9a0 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -14,3 +14,8 @@ proc-macro = true quote = "1.0.26" rand = "0.8.5" syn = { version = "2.0.8", features = ["full"] } + +[dev-dependencies] +trybuild = "1.0.99" +netsim-embed = { path = "..", features = ["ipc"] } +async-std = { version = "1.12.0", features = ["attributes"] } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index ad78c823..fdc4265d 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,4 +1,5 @@ use proc_macro::TokenStream; +use quote::quote; #[proc_macro_attribute] pub fn machine(_attrs: TokenStream, fun: TokenStream) -> TokenStream { @@ -12,10 +13,6 @@ pub fn machine(_attrs: TokenStream, fun: TokenStream) -> TokenStream { f.sig.constness.is_none(), "netsim_embed::machine cannot be const" ); - assert!( - f.sig.asyncness.is_none(), - "netsim_embed::machine cannot be async" - ); assert!( f.sig.unsafety.is_none(), "netsim_embed::machine cannot be unsafe" @@ -28,10 +25,6 @@ pub fn machine(_attrs: TokenStream, fun: TokenStream) -> TokenStream { f.sig.generics.params.is_empty(), "netsim_embed::machine cannot be generic" ); - assert!( - f.sig.inputs.len() == 1, - "netsim_embed::machine must take exactly one argument" - ); assert!( f.sig.variadic.is_none(), "netsim_embed::machine cannot be variadic" @@ -41,23 +34,60 @@ pub fn machine(_attrs: TokenStream, fun: TokenStream) -> TokenStream { "netsim_embed::machine must not declare a return type" ); - let input = match &f.sig.inputs.first().unwrap() { - syn::FnArg::Typed(input) => input, - _ => panic!("netsim_embed::machine must be a freestanding function"), - }; - assert!( - input.attrs.is_empty(), - "netsim_embed::machine's only argument must not have any attributes attached" - ); + let mut inputs = vec![]; + for input in &f.sig.inputs { + match input { + syn::FnArg::Typed(input) => { + assert!( + input.attrs.is_empty(), + "netsim_embed::machine's only argument must not have any attributes attached" + ); + inputs.push(input); + } + _ => panic!("netsim_embed::machine must be a freestanding function"), + } + } let f_vis = f.vis; let f_ident = f.sig.ident; - let input_ty = &input.ty; let id: u128 = rand::random(); - let input_pat = &input.pat; + + let (input_ty, input_pat) = match inputs.len() { + 0 => (quote! { () }, quote! {_}), + 1 => { + let input = inputs.first().unwrap(); + let input_ty = &input.ty; + let input_pat = &input.pat; + (quote! { #input_ty }, quote! { #input_pat }) + } + _ => { + let types: Vec<_> = inputs.iter().map(|x| x.ty.clone()).collect(); + let patterns: Vec<_> = inputs.iter().map(|x| x.pat.clone()).collect(); + + let input_ty = quote! { + (#(#types),*) + }; + let input_pat = quote! { + (#(#patterns),*) + }; + (input_ty, input_pat) + } + }; + let f_block = f.block; + let f_block = if f.sig.asyncness.is_some() { + quote! { + { + async_std::task::block_on(async #f_block) + } + } + } else { + quote! { + #f_block + } + }; - TokenStream::from(quote::quote! { + TokenStream::from(quote! { #[allow(non_camel_case_types)] #f_vis struct #f_ident ; @@ -68,7 +98,14 @@ pub fn machine(_attrs: TokenStream, fun: TokenStream) -> TokenStream { #id } - fn call(#input_pat: #input_ty) #f_block + fn call( #input_pat : #input_ty) #f_block } }) } + +#[test] +fn test() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/macros/failures/*.rs"); + t.pass("tests/macros/success/*.rs"); +} diff --git a/macros/tests/macros/failures/invalid_return.rs b/macros/tests/macros/failures/invalid_return.rs new file mode 100644 index 00000000..e8659baf --- /dev/null +++ b/macros/tests/macros/failures/invalid_return.rs @@ -0,0 +1,6 @@ +#[netsim_embed_macros::machine] +fn foo() -> usize { + 5 +} + +fn main() {} diff --git a/macros/tests/macros/failures/invalid_return.stderr b/macros/tests/macros/failures/invalid_return.stderr new file mode 100644 index 00000000..9579bb3b --- /dev/null +++ b/macros/tests/macros/failures/invalid_return.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> tests/macros/failures/invalid_return.rs:1:1 + | +1 | #[netsim_embed_macros::machine] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: netsim_embed::machine must not declare a return type diff --git a/macros/tests/macros/failures/unsafe_function.rs b/macros/tests/macros/failures/unsafe_function.rs new file mode 100644 index 00000000..ced9a2f5 --- /dev/null +++ b/macros/tests/macros/failures/unsafe_function.rs @@ -0,0 +1,4 @@ +#[netsim_embed_macros::machine] +unsafe fn foo() {} + +fn main() {} diff --git a/macros/tests/macros/failures/unsafe_function.stderr b/macros/tests/macros/failures/unsafe_function.stderr new file mode 100644 index 00000000..c3315564 --- /dev/null +++ b/macros/tests/macros/failures/unsafe_function.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> tests/macros/failures/unsafe_function.rs:1:1 + | +1 | #[netsim_embed_macros::machine] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: netsim_embed::machine cannot be unsafe diff --git a/macros/tests/macros/success/async_call.rs b/macros/tests/macros/success/async_call.rs new file mode 100644 index 00000000..97864011 --- /dev/null +++ b/macros/tests/macros/success/async_call.rs @@ -0,0 +1,15 @@ +use netsim_embed::MachineFn; +use std::sync::OnceLock; + +static DATA: OnceLock = OnceLock::new(); + +#[netsim_embed_macros::machine] +async fn foo(bar: &'static str) { + DATA.get_or_init(|| format!("got {bar}")); +} + +fn main() { + foo::call("hello world"); + let data = DATA.get_or_init(|| "others".to_string()); + assert_eq!(data, "got hello world"); +} diff --git a/macros/tests/macros/success/empty_function.rs b/macros/tests/macros/success/empty_function.rs new file mode 100644 index 00000000..f4b397d9 --- /dev/null +++ b/macros/tests/macros/success/empty_function.rs @@ -0,0 +1,15 @@ +use netsim_embed::MachineFn; +use std::sync::OnceLock; + +static DATA: OnceLock = OnceLock::new(); + +#[netsim_embed_macros::machine] +fn foo() { + DATA.get_or_init(|| "got it".to_string()); +} + +fn main() { + foo::call(()); + let data = DATA.get_or_init(|| "others".to_string()); + assert_eq!(data, "got it"); +} diff --git a/macros/tests/macros/success/multiple_calls.rs b/macros/tests/macros/success/multiple_calls.rs new file mode 100644 index 00000000..efaf3561 --- /dev/null +++ b/macros/tests/macros/success/multiple_calls.rs @@ -0,0 +1,15 @@ +use netsim_embed::MachineFn; +use std::sync::OnceLock; + +static DATA: OnceLock = OnceLock::new(); + +#[netsim_embed_macros::machine] +fn foo(bar: &'static str, baz: &'static str) { + DATA.get_or_init(|| format!("got {bar} {baz}")); +} + +fn main() { + foo::call(("hello", "world")); + let data = DATA.get_or_init(|| "others".to_string()); + assert_eq!(data, "got hello world"); +} diff --git a/macros/tests/macros/success/simple_call.rs b/macros/tests/macros/success/simple_call.rs new file mode 100644 index 00000000..6045f705 --- /dev/null +++ b/macros/tests/macros/success/simple_call.rs @@ -0,0 +1,15 @@ +use netsim_embed::MachineFn; +use std::sync::OnceLock; + +static DATA: OnceLock = OnceLock::new(); + +#[netsim_embed_macros::machine] +fn foo(bar: &'static str) { + DATA.get_or_init(|| format!("got {bar}")); +} + +fn main() { + foo::call("hello world"); + let data = DATA.get_or_init(|| "others".to_string()); + assert_eq!(data, "got hello world"); +}