Skip to content

Commit

Permalink
fix: env macro multiline support
Browse files Browse the repository at this point in the history
  • Loading branch information
LNSD committed Feb 14, 2024
1 parent 4b6497e commit 390a974
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 6 deletions.
162 changes: 156 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ use syn::{Item, ItemStruct, ItemType};
#[cfg(feature = "executable")]
use which::which;

use crate::utils::{fn_macro, is_module, mod_macro};
use crate::utils::{fn_macro, is_module, mod_macro, sanitize_env_vars_attr};

mod utils;

Expand Down Expand Up @@ -130,21 +130,28 @@ pub fn env(attr: TokenStream, stream: TokenStream) -> TokenStream {
}

fn check_env_condition(attr_str: String) -> (bool, String) {
let var_names: Vec<&str> = attr_str.split(',').collect();
let var_names = sanitize_env_vars_attr(&attr_str);

// Check if the environment variables are set
let mut missing_vars = vec![];
for var in var_names.iter() {
if std::env::var(var).is_err() {
missing_vars.push(var.to_string());
for name in var_names {
if std::env::var(name).is_err() {
missing_vars.push(name.to_string());
}
}
let ignore_msg = if missing_vars.len() == 1 {

// Generate ignore message
let ignore_msg = if missing_vars.is_empty() {
String::new()
} else if missing_vars.len() == 1 {
format!("because variable {} not found", missing_vars[0])
} else {
format!(
"because following variables not found:\n{}\n",
missing_vars.join(", ")
)
};

(missing_vars.is_empty(), ignore_msg)
}

Expand Down Expand Up @@ -2520,3 +2527,146 @@ pub fn runtime_ignore_if(attr: TokenStream, stream: TokenStream) -> TokenStream
}
.into()
}

#[cfg(test)]
mod tests {
use super::check_env_condition;

mod env_macro {
use super::*;

#[test]
fn single_env_var_should_be_not_set() {
//* Given
let env_var = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";

// The `test_with::env(<attr_str>)` macro arguments
let attr_str = env_var.to_string();

//* When
let (is_ok, ignore_msg) = check_env_condition(attr_str);

//* Then
// Assert if the test should be ignored
assert!(!is_ok);
// Assert the ignore message should contain only the missing env var names
assert!(ignore_msg.contains(env_var));
}

#[test]
fn multiple_env_vars_should_not_be_set() {
//* Given
let env_var1 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
let env_var2 = "ANOTHER_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";

// The `test_with::env(<attr_str>)` macro arguments
let attr_str = format!("{}, {}", env_var1, env_var2);

//* When
let (is_ok, ignore_msg) = check_env_condition(attr_str);

//* Then
// Assert if the test should be ignored
assert!(!is_ok);
// Assert the ignore message should contain only the missing env var names
assert!(ignore_msg.contains(env_var1));
assert!(ignore_msg.contains(env_var2));
}

#[test]
fn single_env_var_should_be_set() {
//* Given
let env_var = "PATH";

// The `test_with::env(<attr_str>)` macro arguments
let attr_str = env_var.to_string();

//* When
let (is_ok, ignore_msg) = check_env_condition(attr_str);

//* Then
// Assert if the test should be ignored
assert!(is_ok);
// Assert the ignore message should contain only the missing env var names
assert!(!ignore_msg.contains(env_var));
}

/// Test the `test_with::env(<attr_str>)` macro should parse the attribute string correctly
/// when the attribute string contains multiple env vars containing spaces and newlines.
///
/// ```no_run
/// #[test_with::env(
/// PATH,
/// HOME
/// )]
/// #[test]
/// fn some_test() {}
#[test]
fn multiple_env_vars_should_be_set() {
//* Given
let env_var1 = "PATH";
let env_var2 = "HOME";

// The `test_with::env(<attr_str>)` macro arguments
let attr_str = format!("\t{},\n\t{}\n", env_var1, env_var2);

//* When
let (is_ok, ignore_msg) = check_env_condition(attr_str);

//* Then
// Assert if the test should be ignored
assert!(is_ok);
// Assert the ignore message should contain only the missing env var names
assert!(!ignore_msg.contains(env_var1));
assert!(!ignore_msg.contains(env_var2));
}

/// Test the `test_with::env(<attr_str>)` macro should parse the attribute string correctly
/// when the attribute string contains multiple env vars and one of them is not set.
#[test]
fn multiple_env_vars_but_one_is_not_set() {
//* Given
let env_var1 = "PATH";
let env_var2 = "HOME";
let env_var3 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";

// The `test_with::env(<attr_str>)` macro arguments
let attr_str = format!("{}, {}, {}", env_var1, env_var2, env_var3);

//* When
let (is_ok, ignore_msg) = check_env_condition(attr_str);

//* Then
// Assert if the test should be ignored
assert!(!is_ok);
// Assert the ignore message should contain only the missing env var names
assert!(!ignore_msg.contains(env_var1));
assert!(!ignore_msg.contains(env_var2));
assert!(ignore_msg.contains(env_var3));
}

/// Test the `test_with::env(<attr_str>)` macro should parse the attribute string correctly
/// when the attribute string contains multiple env vars and various of them are not set.
#[test]
fn multiple_env_vars_and_various_not_set() {
//* Given
let env_var1 = "PATH";
let env_var2 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
let env_var3 = "ANOHTER_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";

Check warning on line 2655 in src/lib.rs

View workflow job for this annotation

GitHub Actions / lint

"ANOHTER" should be "ANOTHER".

// The `test_with::env(<attr_str>)` macro arguments
let attr_str = format!("{}, {}, {}", env_var1, env_var2, env_var3);

//* When
let (is_ok, ignore_msg) = check_env_condition(attr_str);

//* Then
// Assert if the test should be ignored
assert!(!is_ok);
// Assert the ignore message should contain only the missing env var names
assert!(!ignore_msg.contains(env_var1));
assert!(ignore_msg.contains(env_var2));
assert!(ignore_msg.contains(env_var3));
}
}
}
55 changes: 55 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,58 @@ pub(crate) fn mod_macro(
abort_call_site!("should use on mod with context")
}
}

/// Sanitize the attribute string to remove any leading or trailing whitespace
/// and split the string into an iterator of individual environment variable names.
pub fn sanitize_env_vars_attr(attr_str: &str) -> impl Iterator<Item=&str> {
attr_str.split(',').map(str::trim)
}

#[cfg(test)]
mod tests {
use super::sanitize_env_vars_attr;

#[test]
fn sanitize_single_env_var() {
//* Given
let env_var = "FOO";

let attr_str = env_var.to_string();

//* When
let result = sanitize_env_vars_attr(&attr_str).collect::<Vec<_>>();

//* Then
assert_eq!(result, vec!["FOO"]);
}

#[test]
fn sanitize_multiple_env_vars() {
//* Given
let env_var1 = "FOO";
let env_var2 = "BAR";
let env_var3 = "BAZ";

let attr_str = format!("\t{},\n\t{},\n\t{}", env_var1, env_var2, env_var3);

//* When
let result = sanitize_env_vars_attr(&attr_str).collect::<Vec<_>>();

//* Then
assert_eq!(result, vec!["FOO", "BAR", "BAZ"]);
}

#[test]
fn sanitize_env_vars_with_whitespace() {
//* Given
let env_var = "FOO BAR";

let attr_str = env_var.to_string();

//* When
let result = sanitize_env_vars_attr(&attr_str).collect::<Vec<_>>();

//* Then
assert_eq!(result, vec!["FOO BAR"]);
}
}

0 comments on commit 390a974

Please sign in to comment.