diff --git a/Cargo.lock b/Cargo.lock index f34d4ae4b6..8e7988813b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2266,6 +2266,7 @@ dependencies = [ "frame-metadata 16.0.0", "futures", "hex", + "integration-tests-proc-macro", "parity-scale-codec", "regex", "scale-info", @@ -2284,6 +2285,15 @@ dependencies = [ "wabt", ] +[[package]] +name = "integration-tests-proc-macro" +version = "0.34.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "io-lifetimes" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index d7165f0dfc..16bb64831f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "testing/substrate-runner", "testing/test-runtime", "testing/integration-tests", + "testing/integration-tests/proc-macro", "testing/ui-tests", "testing/generate-custom-metadata", "macro", diff --git a/testing/integration-tests/Cargo.toml b/testing/integration-tests/Cargo.toml index d52ceaa17a..4af5e927e2 100644 --- a/testing/integration-tests/Cargo.toml +++ b/testing/integration-tests/Cargo.toml @@ -46,6 +46,7 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true } wabt = { workspace = true } substrate-runner = { workspace = true } +integration-tests-proc-macro = { path = "proc-macro" } [build-dependencies] cfg_aliases = "0.2.0" diff --git a/testing/integration-tests/proc-macro/Cargo.toml b/testing/integration-tests/proc-macro/Cargo.toml new file mode 100644 index 0000000000..b46bf20bd7 --- /dev/null +++ b/testing/integration-tests/proc-macro/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "integration-tests-proc-macro" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +publish = false + +license.workspace = true +repository.workspace = true +documentation.workspace = true +homepage.workspace = true +description = "Subxt integration tests proc-macros" + +[lib] +proc-macro = true + +[dependencies] +syn = { workspace = true } +proc-macro2 = { workspace = true } +quote = { workspace = true } diff --git a/testing/integration-tests/proc-macro/src/lib.rs b/testing/integration-tests/proc-macro/src/lib.rs new file mode 100644 index 0000000000..fef3b95066 --- /dev/null +++ b/testing/integration-tests/proc-macro/src/lib.rs @@ -0,0 +1,80 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +extern crate proc_macro; +use proc_macro::TokenStream; + +use quote::{format_ident, quote}; +use syn::{ + parse::{Parse, ParseStream}, + Error, +}; + +#[proc_macro_attribute] +pub fn subxt_test(attr: TokenStream, item: TokenStream) -> TokenStream { + let subxt_attr = match syn::parse::(attr) { + Ok(subxt_attr) => subxt_attr, + Err(err) => return err.into_compile_error().into(), + }; + let timeout_duration = subxt_attr.timeout.unwrap_or(60 * 5); + + let func: syn::ItemFn = match syn::parse(item) { + Ok(func) => func, + Err(err) => return err.into_compile_error().into(), + }; + + let func_attrs = &func.attrs; + let func_vis = &func.vis; + let func_sig = &func.sig; + let func_block = &func.block; + let func_output = &func.sig.output; + + let func_name = &func.sig.ident; + let inner_func_name = format_ident!("{}_inner", func_name); + + let result = quote! { + #[tokio::test] + #( #func_attrs )* + #func_vis #func_sig #func_output { + async fn #inner_func_name() #func_output + #func_block + + tokio::time::timeout(std::time::Duration::from_secs(#timeout_duration), #inner_func_name()) + .await + .expect("Test timedout"); + } + }; + result.into() +} + +mod keywords { + syn::custom_keyword!(timeout); +} + +struct SubxtTestAttr { + timeout: Option, +} + +impl Parse for SubxtTestAttr { + fn parse(input: ParseStream) -> Result { + if input.is_empty() { + return Ok(Self { timeout: None }); + } + + let _keyword = input.parse::()?; + input.parse::()?; + let timeout = input.parse::()?.base10_parse::()?; + + if !input.is_empty() { + return Err(Error::new( + input.span(), + "Expected tokens: `timeout = value`", + )); + } + + Ok(Self { + timeout: Some(timeout), + }) + } +} diff --git a/testing/integration-tests/src/utils/mod.rs b/testing/integration-tests/src/utils/mod.rs index dc5215feba..c92d156d62 100644 --- a/testing/integration-tests/src/utils/mod.rs +++ b/testing/integration-tests/src/utils/mod.rs @@ -11,3 +11,14 @@ pub use context::*; pub use node_proc::TestNodeProcess; pub use tx_retries::*; pub use wait_for_blocks::*; + +pub use integration_tests_proc_macro::subxt_test; + +/// The test timeout is set to 1 second. +/// However, the test is sleeping for 5 seconds. +/// This must cause the test to panic. +#[subxt_test(timeout = 1)] +#[should_panic] +async fn test_subxt_macro() { + tokio::time::sleep(std::time::Duration::from_secs(5)).await; +}