Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add runtime_file, runtime_path #66

Merged
merged 5 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,8 @@ jobs:
steps:
- uses: actions/checkout@v2

- uses: cachix/install-nix-action@v15
with:
nix_path: nixpkgs=channel:nixos-unstable

- name: Install typos
run: nix develop -c 'cargo' install typos-cli

- name: Check typo
run: nix develop -c 'typos' --exclude ssvm-evmc
- name: Check spelling of file.txt
uses: crate-ci/typos@master

- name: Super-Linter
uses: github/super-linter@v4
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ executable = ["which"]
[dev-dependencies]
tokio = { version = "1.15.0", features = ["rt", "macros"] }
serial_test = "2.0.0"

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
20 changes: 18 additions & 2 deletions examples/runner/examples/test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
test_with::runner!(env);
test_with::runner!(env, file, path);

#[test_with::module]
mod env {
Expand Down Expand Up @@ -39,6 +39,22 @@ mod env {

#[test_with::runtime_no_env(GITHUB_ACTIONS)]
fn test_ignore_in_github_action() {
//This will be ignored in GITHUB_ACTION;
panic!("should be ignored in github action")
}
}

#[test_with::module]
mod file {
#[test_with::runtime_file(/etc/hostname)]
fn test_works() {
assert!(true);
}
}

#[test_with::module]
mod path {
#[test_with::runtime_path(/no_existing)]
fn test_not_works() {
assert!(true);
}
}
201 changes: 199 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
//! as an example, then use `cargo run --example`
//! The `test-with` need be included as normal dependency with `runtime` feature.
//! [macro@runner] and [macro@module] are for the basic skeleton of the test runner.
//! [macro@runtime_env], [macro@runtime_no_env] are used to transform a normal function to a
//! [macro@runtime_env], [macro@runtime_no_env], [macro@runtime_file] and [macro@runtime_path] are
//! used to transform a normal function to a
//! testcase.
//!
//! ```toml
Expand Down Expand Up @@ -141,6 +142,7 @@ fn check_env_condition(attr_str: String) -> (bool, String) {
(missing_vars.is_empty(), ignore_msg)
}

/// Run test case when the example running and the environment variable is set.
///```rust
/// // write as example in exmaples/*rs
/// test_with::runner!(env);
Expand All @@ -152,6 +154,12 @@ fn check_env_condition(attr_str: String) -> (bool, String) {
/// }
/// }
///```
#[cfg(not(feature = "runtime"))]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn runtime_env(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
panic!("should be used with runtime feature")
}
#[cfg(feature = "runtime")]
#[proc_macro_attribute]
#[proc_macro_error]
Expand Down Expand Up @@ -240,6 +248,7 @@ fn check_no_env_condition(attr_str: String) -> (bool, String) {
(true, String::new())
}

/// Ignore test case when the example running and the environment variable is set.
///```rust
/// // write as example in exmaples/*rs
/// test_with::runner!(env);
Expand All @@ -251,6 +260,12 @@ fn check_no_env_condition(attr_str: String) -> (bool, String) {
/// }
/// }
///```
#[cfg(not(feature = "runtime"))]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn runtime_no_env(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
panic!("should be used with runtime feature")
}
#[cfg(feature = "runtime")]
#[proc_macro_attribute]
#[proc_macro_error]
Expand Down Expand Up @@ -360,6 +375,70 @@ fn check_file_condition(attr_str: String) -> (bool, String) {
(missing_files.is_empty(), ignore_msg)
}

/// Run test case when the example running and the file exist.
///```rust
/// // write as example in exmaples/*rs
/// test_with::runner!(file);
/// #[test_with::module]
/// mod file {
/// #[test_with::runtime_file(/etc/hostname)]
/// fn test_works() {
/// assert!(true);
/// }
/// }
///```
#[cfg(not(feature = "runtime"))]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn runtime_file(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
panic!("should be used with runtime feature")
}
#[cfg(feature = "runtime")]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn runtime_file(attr: TokenStream, stream: TokenStream) -> TokenStream {
let attr_str = attr.to_string().replace(' ', "");
let files: Vec<&str> = attr_str.split(',').collect();
let ItemFn {
attrs,
vis,
sig,
block,
} = parse_macro_input!(stream as ItemFn);
let syn::Signature { ident, .. } = sig.clone();
let check_ident = syn::Ident::new(
&format!("_check_{}", ident.to_string()),
proc_macro2::Span::call_site(),
);
quote::quote! {
fn #check_ident() -> Result<(), libtest_with::Failed> {
let mut missing_files = vec![];
#(
if !std::path::Path::new(#files.trim_matches('"')).is_file() {
missing_files.push(#files);
}
)*

match missing_files.len() {
0 => #ident(),
1 => return Err(
format!("{}because file not found: {}",
libtest_with::RUNTIME_IGNORE_PREFIX, missing_files[0]
).into()),
_ => return Err(
format!("{}because following files not found: \n{}\n",
libtest_with::RUNTIME_IGNORE_PREFIX, missing_files.join(", ")
).into()),
}
Ok(())
}

#(#attrs)*
#vis #sig #block
}
.into()
}

/// Run test case when the path(file or folder) exist.
/// ```
/// #[cfg(test)]
Expand Down Expand Up @@ -424,6 +503,70 @@ fn check_path_condition(attr_str: String) -> (bool, String) {
(missing_paths.is_empty(), ignore_msg)
}

/// Run test case when the example running and the path(file or folder) exist.
///```rust
/// // write as example in exmaples/*rs
/// test_with::runner!(path);
/// #[test_with::module]
/// mod path {
/// #[test_with::runtime_path(/etc)]
/// fn test_works() {
/// assert!(true);
/// }
/// }
///```
#[cfg(not(feature = "runtime"))]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn runtime_path(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
panic!("should be used with runtime feature")
}
#[cfg(feature = "runtime")]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn runtime_path(attr: TokenStream, stream: TokenStream) -> TokenStream {
let attr_str = attr.to_string().replace(' ', "");
let paths: Vec<&str> = attr_str.split(',').collect();
let ItemFn {
attrs,
vis,
sig,
block,
} = parse_macro_input!(stream as ItemFn);
let syn::Signature { ident, .. } = sig.clone();
let check_ident = syn::Ident::new(
&format!("_check_{}", ident.to_string()),
proc_macro2::Span::call_site(),
);
quote::quote! {
fn #check_ident() -> Result<(), libtest_with::Failed> {
let mut missing_paths = vec![];
#(
if std::fs::metadata(#paths.trim_matches('"')).is_err() {
missing_paths.push(#paths.to_string());
}
)*

match missing_paths.len() {
0 => #ident(),
1 => return Err(
format!("{}because path not found: {}",
libtest_with::RUNTIME_IGNORE_PREFIX, missing_paths[0]
).into()),
_ => return Err(
format!("{}because following paths not found: \n{}\n",
libtest_with::RUNTIME_IGNORE_PREFIX, missing_paths.join(", ")
).into()),
}
Ok(())
}

#(#attrs)*
#vis #sig #block
}
.into()
}

/// Run test case when the http service exist.
/// ```
/// #[cfg(test)]
Expand Down Expand Up @@ -1077,13 +1220,39 @@ fn check_executable_condition(attr_str: String) -> (bool, String) {
(missing_executables.is_empty(), ignore_msg)
}

/// Provide a test runner and test on each module
///```rust
/// // example/run-test.rs
///
/// test_with::runner!(module1, module2);
/// #[test_with::module]
/// mod module1 {
/// #[test_with::runtime_env(PWD)]
/// fn test_works() {
/// assert!(true);
/// }
/// }
///
/// #[test_with::module]
/// mod module2 {
/// #[test_with::runtime_env(PWD)]
/// fn test_works() {
/// assert!(true);
/// }
/// }
///```
#[cfg(not(feature = "runtime"))]
#[proc_macro]
pub fn runner(_input: TokenStream) -> TokenStream {
panic!("should be used with runtime feature")
}
#[cfg(feature = "runtime")]
#[proc_macro]
pub fn runner(input: TokenStream) -> TokenStream {
let input_str = input.to_string();
let mod_names: Vec<syn::Ident> = input_str
.split(",")
.map(|s| syn::Ident::new(s, proc_macro2::Span::call_site()))
.map(|s| syn::Ident::new(s.trim(), proc_macro2::Span::call_site()))
.collect();
quote::quote! {
fn main() {
Expand All @@ -1098,6 +1267,34 @@ pub fn runner(input: TokenStream) -> TokenStream {
.into()
}

/// Help each function with `#[test_with::runtime_*]` in the module can register to run
///
///```rust
/// // example/run-test.rs
///
/// test_with::runner!(module1, module2);
/// #[test_with::module]
/// mod module1 {
/// #[test_with::runtime_env(PWD)]
/// fn test_works() {
/// assert!(true);
/// }
/// }
///
/// #[test_with::module]
/// mod module2 {
/// #[test_with::runtime_env(PWD)]
/// fn test_works() {
/// assert!(true);
/// }
/// }
///```
#[cfg(not(feature = "runtime"))]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn module(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
panic!("should be used with runtime feature")
}
#[cfg(feature = "runtime")]
#[proc_macro_attribute]
#[proc_macro_error]
Expand Down
Loading