-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add async test helper to timeout and provide a task_executor automatically #6651
Changes from 26 commits
476c881
8117934
ce5ea42
34c0f23
544c1d2
2796798
a0535f7
b86cc8e
6b2f66f
2a47de6
24a165b
0507b2f
f173c1c
8e45871
b7e1b1d
a200f34
c157c22
d074de0
f0c0e7f
3a5d4f0
40d1804
a8c62ff
82ceab0
badbec3
a9d03e1
c7c3e52
78037b8
0587d76
e4b9ff1
948c64c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| [package] | ||
| name = "substrate-test-utils-derive" | ||
| version = "0.8.0-rc4" | ||
cecton marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| authors = ["Parity Technologies <[email protected]>"] | ||
| edition = "2018" | ||
| license = "Apache-2.0" | ||
| homepage = "https://substrate.dev" | ||
| repository = "https://github.com/paritytech/substrate/" | ||
|
|
||
| [dependencies] | ||
| quote = "1.0.6" | ||
| syn = { version = "1.0.33", features = ["full"] } | ||
| proc-macro-crate = "0.1.4" | ||
|
|
||
| [lib] | ||
| proc-macro = true | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2020 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| use proc_macro::{Span, TokenStream}; | ||
| use proc_macro_crate::crate_name; | ||
| use quote::quote; | ||
| use std::env; | ||
|
|
||
| #[proc_macro_attribute] | ||
| pub fn test(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| impl_test(args, item) | ||
| } | ||
|
|
||
| fn impl_test(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| let input = syn::parse_macro_input!(item as syn::ItemFn); | ||
| let args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
|
|
||
| parse_knobs(input, args).unwrap_or_else(|e| e.to_compile_error().into()) | ||
| } | ||
|
|
||
| fn parse_knobs( | ||
| mut input: syn::ItemFn, | ||
| args: syn::AttributeArgs, | ||
| ) -> Result<TokenStream, syn::Error> { | ||
| let sig = &mut input.sig; | ||
| let body = &input.block; | ||
| let attrs = &input.attrs; | ||
| let vis = input.vis; | ||
|
|
||
| if sig.inputs.len() != 1 { | ||
| let msg = "the test function accepts only one argument of type sc_service::TaskExecutor"; | ||
| return Err(syn::Error::new_spanned(&sig, msg)); | ||
| } | ||
| let (task_executor_name, task_executor_type) = match sig.inputs.pop().map(|x| x.into_value()) { | ||
| Some(syn::FnArg::Typed(x)) => (x.pat, x.ty), | ||
| _ => { | ||
| let msg = | ||
| "the test function accepts only one argument of type sc_service::TaskExecutor"; | ||
| return Err(syn::Error::new_spanned(&sig, msg)); | ||
| } | ||
| }; | ||
|
|
||
| let crate_name = if env::var("CARGO_PKG_NAME").unwrap() == "substrate-test-utils" { | ||
| syn::Ident::new("substrate_test_utils", Span::call_site().into()) | ||
| } else { | ||
| let crate_name = crate_name("substrate-test-utils") | ||
| .map_err(|e| syn::Error::new_spanned(&sig, e))?; | ||
|
|
||
| syn::Ident::new(&crate_name, Span::call_site().into()) | ||
| }; | ||
|
|
||
| let header = { | ||
| quote! { | ||
| #[#crate_name::tokio::test(#(#args)*)] | ||
| } | ||
| }; | ||
|
|
||
| let result = quote! { | ||
| #header | ||
| #(#attrs)* | ||
| #vis #sig { | ||
| use #crate_name::futures::future::FutureExt; | ||
|
|
||
| let #task_executor_name: #task_executor_type = (|fut, _| { | ||
| #crate_name::tokio::spawn(fut).map(drop) | ||
| }) | ||
| .into(); | ||
| let timeout_task = #crate_name::tokio::time::delay_for( | ||
| std::time::Duration::from_secs( | ||
| std::env::var("SUBSTRATE_TEST_TIMEOUT") | ||
| .ok() | ||
| .and_then(|x| x.parse().ok()) | ||
| .unwrap_or(600)) | ||
| ).fuse(); | ||
| let actual_test_task = async move { | ||
| #body | ||
| } | ||
| .fuse(); | ||
|
|
||
| #crate_name::futures::pin_mut!(timeout_task, actual_test_task); | ||
|
|
||
| #crate_name::futures::select! { | ||
| _ = timeout_task => { | ||
| panic!("the test took too long"); | ||
cecton marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }, | ||
| _ = actual_test_task => {}, | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| Ok(result.into()) | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -17,6 +17,27 @@ | |||
|
|
||||
| //! Test utils | ||||
| #[doc(hidden)] | ||||
| pub use futures; | ||||
| /// Marks async function to be executed by an async runtime and provide a `TaskExecutor`, suitable | ||||
| /// to test environment. | ||||
| /// | ||||
| /// # Example | ||||
cecton marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| /// | ||||
| /// ``` | ||||
bkchr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| /// use tokio; // WARNING: you must have tokio in the dependency of your crate to use this macro! | ||||
|
||||
| /// use tokio; // WARNING: you must have tokio in the dependency of your crate to use this macro! |
This should be documented in the text above!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done 0587d76
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed typo: e4b9ff1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// |
bkchr marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| [package] | ||
| name = "substrate-test-utils-test-crate" | ||
| version = "0.1.0" | ||
| authors = ["Parity Technologies <[email protected]>"] | ||
| edition = "2018" | ||
| license = "Apache-2.0" | ||
| homepage = "https://substrate.dev" | ||
| repository = "https://github.com/paritytech/substrate/" | ||
|
|
||
| [package.metadata.docs.rs] | ||
| targets = ["x86_64-unknown-linux-gnu"] | ||
|
|
||
| [dev-dependencies] | ||
| tokio = { version = "0.2.13", features = ["macros"] } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bkchr I made this test-crate to check if it worked but I found out that tokio relies on the extern name tokio. The rest is fine and working though. I inspired my code from what you provided but I simplified a bit. (I have not done the generate_crate_access yet!)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what generate_crate_access is doing/solving. Do you have a small & quick explanation? |
||
| test-utils = { path = "..", package = "substrate-test-utils" } | ||
| sc-service = { path = "../../client/service" } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2020 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| #[cfg(test)] | ||
| #[test_utils::test] | ||
| async fn basic_test(_: sc_service::TaskExecutor) { | ||
| assert!(true); | ||
| } | ||
|
|
||
| fn main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2020 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| use sc_service::{TaskExecutor, TaskType}; | ||
|
|
||
| #[substrate_test_utils::test] | ||
| async fn basic_test(_: TaskExecutor) { | ||
| assert!(true); | ||
| } | ||
|
|
||
| #[substrate_test_utils::test] | ||
| #[should_panic(expected = "boo!")] | ||
| async fn panicking_test(_: TaskExecutor) { | ||
| panic!("boo!"); | ||
| } | ||
|
|
||
| #[substrate_test_utils::test(max_threads = 2)] | ||
| async fn basic_test_with_args(_: TaskExecutor) { | ||
| assert!(true); | ||
| } | ||
|
|
||
| #[substrate_test_utils::test] | ||
| async fn rename_argument(ex: TaskExecutor) { | ||
| let ex2 = ex.clone(); | ||
| ex2.spawn(Box::pin(async { () }), TaskType::Blocking); | ||
| assert!(true); | ||
| } | ||
|
|
||
| #[substrate_test_utils::test] | ||
| #[should_panic(expected = "test took too long")] | ||
| // NOTE: enable this test only after setting SUBSTRATE_TEST_TIMEOUT to a smaller value | ||
| // | ||
| // SUBSTRATE_TEST_TIMEOUT=1 cargo test -- --ignored timeout | ||
| #[ignore] | ||
| async fn timeout(_: TaskExecutor) { | ||
| tokio::time::delay_for(std::time::Duration::from_secs( | ||
| std::env::var("SUBSTRATE_TEST_TIMEOUT") | ||
| .expect("env var SUBSTRATE_TEST_TIMEOUT has been provided by the user") | ||
| .parse::<u64>() | ||
| .unwrap() + 1, | ||
| )) | ||
| .await; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2020 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| #[test] | ||
| fn substrate_test_utils_derive_trybuild() { | ||
| let t = trybuild::TestCases::new(); | ||
| t.compile_fail("tests/ui/missing-func-parameter.rs"); | ||
| t.compile_fail("tests/ui/too-many-func-parameters.rs"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) 2020 Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| #[substrate_test_utils::test] | ||
| async fn missing_func_parameter() { | ||
| assert!(true); | ||
| } | ||
|
|
||
| fn main() {} |
Uh oh!
There was an error while loading. Please reload this page.