diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/serial_test_derive | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/serial_test_derive')
7 files changed, 543 insertions, 0 deletions
diff --git a/third_party/rust/serial_test_derive/.cargo-checksum.json b/third_party/rust/serial_test_derive/.cargo-checksum.json new file mode 100644 index 0000000000..313de515d5 --- /dev/null +++ b/third_party/rust/serial_test_derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"a02b6139f1d683aff6731643e41aaf0ffb5d8ea7e186f9e2bbdbcba5a4837f7d","LICENSE":"ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6","README.md":"b6df6928aece5915ef3469e8384c3716a4fe58820fd1e8ab758c061bd5a499f0","src/lib.rs":"0881974b990368dd1fabcbb97b5f75e861f1acbf150f0fe740abf8a4576c80db","tests/broken/test_serial_async_before_wrapper.rs":"fc61e416a5ec885af93e2e846323ca5bc6ca7c2e7e315c26e1f7f035b029b96e","tests/broken/test_serial_async_before_wrapper.stderr":"139864bee445d9ef9d554d4c261c657dda8c864b402e1d6dd66bdde3037c3fe8"},"package":"2881bccd7d60fb32dfa3d7b3136385312f8ad75e2674aab2852867a09790cae8"}
\ No newline at end of file diff --git a/third_party/rust/serial_test_derive/Cargo.toml b/third_party/rust/serial_test_derive/Cargo.toml new file mode 100644 index 0000000000..95fe5bcfe9 --- /dev/null +++ b/third_party/rust/serial_test_derive/Cargo.toml @@ -0,0 +1,44 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "serial_test_derive" +version = "0.6.0" +authors = ["Tom Parker-Shemilt <palfrey@tevp.net>"] +description = "Helper crate for serial_test" +readme = "README.md" +categories = ["development-tools::testing"] +license = "MIT" +repository = "https://github.com/palfrey/serial_test/" + +[lib] +proc-macro = true +[dependencies.proc-macro-error] +version = "1" + +[dependencies.proc-macro2] +version = "1.0" + +[dependencies.quote] +version = "1.0" + +[dependencies.rustversion] +version = "1.0" + +[dependencies.syn] +version = "1.0" +features = ["full"] +[dev-dependencies.env_logger] +version = ">= 0.7, <0.9" + +[dev-dependencies.trybuild] +version = "1" diff --git a/third_party/rust/serial_test_derive/LICENSE b/third_party/rust/serial_test_derive/LICENSE new file mode 100644 index 0000000000..5a8adc1d4d --- /dev/null +++ b/third_party/rust/serial_test_derive/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Tom Parker-Shemilt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
\ No newline at end of file diff --git a/third_party/rust/serial_test_derive/README.md b/third_party/rust/serial_test_derive/README.md new file mode 100644 index 0000000000..d379a1d2fd --- /dev/null +++ b/third_party/rust/serial_test_derive/README.md @@ -0,0 +1,51 @@ +# serial_test +[![Version](https://img.shields.io/crates/v/serial_test.svg)](https://crates.io/crates/serial_test) +[![Downloads](https://img.shields.io/crates/d/serial_test)](https://crates.io/crates/serial_test) +[![Docs](https://docs.rs/serial_test/badge.svg)](https://docs.rs/serial_test/) +[![MIT license](https://img.shields.io/crates/l/serial_test.svg)](./LICENSE) +[![Build Status](https://github.com/palfrey/serial_test/workflows/Continuous%20integration/badge.svg?branch=main)](https://github.com/palfrey/serial_test/actions) +[![MSRV: 1.39.0](https://flat.badgen.net/badge/MSRV/1.39.0/purple)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) + +`serial_test` allows for the creation of serialised Rust tests using the `serial` attribute +e.g. +```rust +#[test] +#[serial] +fn test_serial_one() { + // Do things +} + +#[test] +#[serial] +fn test_serial_another() { + // Do things +} + +#[tokio::test] +#[serial] +async fn test_serial_another() { + // Do things asynchronously +} +``` +Multiple tests with the `serial` attribute are guaranteed to be executed in serial. Ordering of the tests is not guaranteed however. +Tests without the `serial` attribute may run at any time, including in parallel to tests marked as `serial`. Note that if you're using +an async test reactor attribute (e.g. `tokio::test` or `actix_rt::test`) then they should be listed *before* `serial`, otherwise we +don't get an async function and things break. There's now an error for this case to improve debugging. + +## Usage +We require at least Rust 1.39 for [async/await](https://blog.rust-lang.org/2019/11/07/Async-await-stable.html) support + +Add to your Cargo.toml +```toml +[dev-dependencies] +serial_test = "*" +``` + +plus `use serial_test::serial;` (for Rust 2018) or +```rust +#[macro_use] +extern crate serial_test; +``` +for earlier versions. + +You can then either add `#[serial]` or `#[serial(some_text)]` to tests as required. diff --git a/third_party/rust/serial_test_derive/src/lib.rs b/third_party/rust/serial_test_derive/src/lib.rs new file mode 100644 index 0000000000..3e827cb967 --- /dev/null +++ b/third_party/rust/serial_test_derive/src/lib.rs @@ -0,0 +1,414 @@ +//! # serial_test_derive +//! Helper crate for [serial_test](../serial_test/index.html) + +#![cfg_attr(docsrs, feature(doc_cfg))] + +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::TokenTree; +use proc_macro_error::{abort_call_site, proc_macro_error}; +use quote::{format_ident, quote, ToTokens, TokenStreamExt}; +use std::ops::Deref; + +/// Allows for the creation of serialised Rust tests +/// ```` +/// #[test] +/// #[serial] +/// fn test_serial_one() { +/// // Do things +/// } +/// +/// #[test] +/// #[serial] +/// fn test_serial_another() { +/// // Do things +/// } +/// ```` +/// Multiple tests with the [serial](macro@serial) attribute are guaranteed to be executed in serial. Ordering +/// of the tests is not guaranteed however. If you want different subsets of tests to be serialised with each +/// other, but not depend on other subsets, you can add an argument to [serial](macro@serial), and all calls +/// with identical arguments will be called in serial. e.g. +/// ```` +/// #[test] +/// #[serial(something)] +/// fn test_serial_one() { +/// // Do things +/// } +/// +/// #[test] +/// #[serial(something)] +/// fn test_serial_another() { +/// // Do things +/// } +/// +/// #[test] +/// #[serial(other)] +/// fn test_serial_third() { +/// // Do things +/// } +/// +/// #[test] +/// #[serial(other)] +/// fn test_serial_fourth() { +/// // Do things +/// } +/// ```` +/// `test_serial_one` and `test_serial_another` will be executed in serial, as will `test_serial_third` and `test_serial_fourth` +/// but neither sequence will be blocked by the other +/// +/// Nested serialised tests (i.e. a [serial](macro@serial) tagged test calling another) is supported +#[proc_macro_attribute] +#[proc_macro_error] +pub fn serial(attr: TokenStream, input: TokenStream) -> TokenStream { + local_serial_core(attr.into(), input.into()).into() +} + +/// Allows for the creation of file-serialised Rust tests +/// ```` +/// #[test] +/// #[file_serial] +/// fn test_serial_one() { +/// // Do things +/// } +/// +/// #[test] +/// #[file_serial] +/// fn test_serial_another() { +/// // Do things +/// } +/// ```` +/// +/// Multiple tests with the [file_serial](macro@file_serial) attribute are guaranteed to run in serial, as per the [serial](macro@serial) +/// attribute. Note that there are no guarantees about one test with [serial](macro@serial) and another with [file_serial](macro@file_serial) +/// as they lock using different methods, and [file_serial](macro@file_serial) does not support nested serialised tests, but otherwise acts +/// like [serial](macro@serial). +/// +/// It also supports an optional `path` arg e.g +/// ```` +/// #[test] +/// #[file_serial(key, "/tmp/foo")] +/// fn test_serial_one() { +/// // Do things +/// } +/// +/// #[test] +/// #[file_serial(key, "/tmp/foo")] +/// fn test_serial_another() { +/// // Do things +/// } +/// ```` +/// Note that in this case you need to specify the `name` arg as well (as per [serial](macro@serial)). The path defaults to a reasonable temp directory for the OS if not specified. +#[proc_macro_attribute] +#[proc_macro_error] +#[cfg_attr(docsrs, doc(cfg(feature = "file_locks")))] +pub fn file_serial(attr: TokenStream, input: TokenStream) -> TokenStream { + fs_serial_core(attr.into(), input.into()).into() +} + +// Based off of https://github.com/dtolnay/quote/issues/20#issuecomment-437341743 +struct QuoteOption<T>(Option<T>); + +impl<T: ToTokens> ToTokens for QuoteOption<T> { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + tokens.append_all(match self.0 { + Some(ref t) => quote! { ::std::option::Option::Some(#t) }, + None => quote! { ::std::option::Option::None }, + }); + } +} + +fn get_raw_args(attr: proc_macro2::TokenStream) -> Vec<String> { + let mut attrs = attr.into_iter().collect::<Vec<TokenTree>>(); + let mut raw_args: Vec<String> = Vec::new(); + while !attrs.is_empty() { + match attrs.remove(0) { + TokenTree::Ident(id) => { + raw_args.push(id.to_string()); + } + TokenTree::Literal(literal) => { + let string_literal = literal.to_string(); + if !string_literal.starts_with('\"') || !string_literal.ends_with('\"') { + panic!("Expected a string literal, got '{}'", string_literal); + } + // Hacky way of getting a string without the enclosing quotes + raw_args.push(string_literal[1..string_literal.len() - 1].to_string()); + } + x => { + panic!("Expected either strings or literals as args, not {}", x); + } + } + if !attrs.is_empty() { + match attrs.remove(0) { + TokenTree::Punct(p) if p.as_char() == ',' => {} + x => { + panic!("Expected , between args, not {}", x); + } + } + } + } + raw_args +} + +fn local_serial_core( + attr: proc_macro2::TokenStream, + input: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let mut raw_args = get_raw_args(attr); + let key = match raw_args.len() { + 0 => "".to_string(), + 1 => raw_args.pop().unwrap(), + n => { + panic!( + "Expected either 0 or 1 arguments, got {}: {:?}", + n, raw_args + ); + } + }; + serial_setup(input, vec![Box::new(key)], "local") +} + +fn fs_serial_core( + attr: proc_macro2::TokenStream, + input: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let none_ident = Box::new(format_ident!("None")); + let mut args: Vec<Box<dyn quote::ToTokens>> = Vec::new(); + let mut raw_args = get_raw_args(attr); + match raw_args.len() { + 0 => { + args.push(Box::new("".to_string())); + args.push(none_ident); + } + 1 => { + args.push(Box::new(raw_args.pop().unwrap())); + args.push(none_ident); + } + 2 => { + let key = raw_args.remove(0); + let path = raw_args.remove(0); + args.push(Box::new(key)); + args.push(Box::new(QuoteOption(Some(path)))); + } + n => { + panic!("Expected 0-2 arguments, got {}: {:?}", n, raw_args); + } + } + serial_setup(input, args, "fs") +} + +fn serial_setup<T>( + input: proc_macro2::TokenStream, + args: Vec<Box<T>>, + prefix: &str, +) -> proc_macro2::TokenStream +where + T: quote::ToTokens + ?Sized, +{ + let ast: syn::ItemFn = syn::parse2(input).unwrap(); + let asyncness = ast.sig.asyncness; + let name = ast.sig.ident; + let return_type = match ast.sig.output { + syn::ReturnType::Default => None, + syn::ReturnType::Type(_rarrow, ref box_type) => Some(box_type.deref()), + }; + let block = ast.block; + let attrs: Vec<syn::Attribute> = ast + .attrs + .into_iter() + .filter(|at| { + if let Ok(m) = at.parse_meta() { + let path = m.path(); + if asyncness.is_some() + && path.segments.len() == 2 + && path.segments[1].ident == "test" + { + // We assume that any 2-part attribute with the second part as "test" on an async function + // is the "do this test with reactor" wrapper. This is true for actix, tokio and async_std. + abort_call_site!("Found async test attribute after serial, which will break"); + } + + // we skip ignore/should_panic because the test framework already deals with it + !(path.is_ident("ignore") || path.is_ident("should_panic")) + } else { + true + } + }) + .collect(); + if let Some(ret) = return_type { + match asyncness { + Some(_) => { + let fnname = format_ident!("{}_async_serial_core_with_return", prefix); + quote! { + #(#attrs) + * + async fn #name () -> #ret { + serial_test::#fnname(#(#args ),*, || async #block ).await; + } + } + } + None => { + let fnname = format_ident!("{}_serial_core_with_return", prefix); + quote! { + #(#attrs) + * + fn #name () -> #ret { + serial_test::#fnname(#(#args ),*, || #block ) + } + } + } + } + } else { + match asyncness { + Some(_) => { + let fnname = format_ident!("{}_async_serial_core", prefix); + quote! { + #(#attrs) + * + async fn #name () { + serial_test::#fnname(#(#args ),*, || async #block ).await; + } + } + } + None => { + let fnname = format_ident!("{}_serial_core", prefix); + quote! { + #(#attrs) + * + fn #name () { + serial_test::#fnname(#(#args ),*, || #block ); + } + } + } + } + } +} + +#[cfg(test)] +mod tests { + use proc_macro2::{Literal, Punct, Spacing}; + + use super::{format_ident, fs_serial_core, local_serial_core, quote, TokenTree}; + use std::iter::FromIterator; + + #[test] + fn test_serial() { + let attrs = proc_macro2::TokenStream::new(); + let input = quote! { + #[test] + fn foo() {} + }; + let stream = local_serial_core(attrs.into(), input); + let compare = quote! { + #[test] + fn foo () { + serial_test::local_serial_core("", || {} ); + } + }; + assert_eq!(format!("{}", compare), format!("{}", stream)); + } + + #[test] + fn test_stripped_attributes() { + let _ = env_logger::builder().is_test(true).try_init(); + let attrs = proc_macro2::TokenStream::new(); + let input = quote! { + #[test] + #[ignore] + #[should_panic(expected = "Testing panic")] + #[something_else] + fn foo() {} + }; + let stream = local_serial_core(attrs.into(), input); + let compare = quote! { + #[test] + #[something_else] + fn foo () { + serial_test::local_serial_core("", || {} ); + } + }; + assert_eq!(format!("{}", compare), format!("{}", stream)); + } + + #[test] + fn test_serial_async() { + let attrs = proc_macro2::TokenStream::new(); + let input = quote! { + async fn foo() {} + }; + let stream = local_serial_core(attrs.into(), input); + let compare = quote! { + async fn foo () { + serial_test::local_async_serial_core("", || async {} ).await; + } + }; + assert_eq!(format!("{}", compare), format!("{}", stream)); + } + + #[test] + fn test_serial_async_return() { + let attrs = proc_macro2::TokenStream::new(); + let input = quote! { + async fn foo() -> Result<(), ()> { Ok(()) } + }; + let stream = local_serial_core(attrs.into(), input); + let compare = quote! { + async fn foo () -> Result<(), ()> { + serial_test::local_async_serial_core_with_return("", || async { Ok(()) } ).await; + } + }; + assert_eq!(format!("{}", compare), format!("{}", stream)); + } + + // 1.54 needed for https://github.com/rust-lang/rust/commit/9daf546b77dbeab7754a80d7336cd8d00c6746e4 change in note message + #[rustversion::since(1.54)] + #[test] + fn test_serial_async_before_wrapper() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/broken/test_serial_async_before_wrapper.rs"); + } + + #[test] + fn test_file_serial() { + let attrs = vec![TokenTree::Ident(format_ident!("foo"))]; + let input = quote! { + #[test] + fn foo() {} + }; + let stream = fs_serial_core( + proc_macro2::TokenStream::from_iter(attrs.into_iter()), + input, + ); + let compare = quote! { + #[test] + fn foo () { + serial_test::fs_serial_core("foo", None, || {} ); + } + }; + assert_eq!(format!("{}", compare), format!("{}", stream)); + } + + #[test] + fn test_file_serial_with_path() { + let attrs = vec![ + TokenTree::Ident(format_ident!("foo")), + TokenTree::Punct(Punct::new(',', Spacing::Alone)), + TokenTree::Literal(Literal::string("bar_path")), + ]; + let input = quote! { + #[test] + fn foo() {} + }; + let stream = fs_serial_core( + proc_macro2::TokenStream::from_iter(attrs.into_iter()), + input, + ); + let compare = quote! { + #[test] + fn foo () { + serial_test::fs_serial_core("foo", ::std::option::Option::Some("bar_path"), || {} ); + } + }; + assert_eq!(format!("{}", compare), format!("{}", stream)); + } +} diff --git a/third_party/rust/serial_test_derive/tests/broken/test_serial_async_before_wrapper.rs b/third_party/rust/serial_test_derive/tests/broken/test_serial_async_before_wrapper.rs new file mode 100644 index 0000000000..690eccf591 --- /dev/null +++ b/third_party/rust/serial_test_derive/tests/broken/test_serial_async_before_wrapper.rs @@ -0,0 +1,7 @@ +use serial_test_derive::serial; + +#[serial] +#[actix_rt::test] +async fn test_async_serial_no_arg_actix() {} + +fn main() {}
\ No newline at end of file diff --git a/third_party/rust/serial_test_derive/tests/broken/test_serial_async_before_wrapper.stderr b/third_party/rust/serial_test_derive/tests/broken/test_serial_async_before_wrapper.stderr new file mode 100644 index 0000000000..f37e867bba --- /dev/null +++ b/third_party/rust/serial_test_derive/tests/broken/test_serial_async_before_wrapper.stderr @@ -0,0 +1,7 @@ +error: Found async test attribute after serial, which will break + --> tests/broken/test_serial_async_before_wrapper.rs:3:1 + | +3 | #[serial] + | ^^^^^^^^^ + | + = note: this error originates in the attribute macro `serial` (in Nightly builds, run with -Z macro-backtrace for more info) |