#[cfg(not(doc))]
#[macro_export]
macro_rules! __extel_init_tests {
($($test:expr),*) => {{
#[allow(unused_mut)]
let mut v: Vec<$crate::Test> = Vec::new();
$(let test_name: &'static str = stringify!($test);
let test_fn: fn() -> Box<dyn $crate::GenericTestResult> = || Box::new($test());
v.push($crate::Test { test_name, test_fn });)*
v
}};
}
#[macro_export]
macro_rules! pass {
() => {
Ok(())
};
}
#[macro_export]
macro_rules! fail {
($fmt:expr, $($arg:expr),*) => {
Result::<(), $crate::errors::Error>::Err($crate::err!($fmt, $($arg),*))
};
($fmt:expr) => { Result::<(), $crate::errors::Error>::Err($crate::err!($fmt)) }
}
#[macro_export]
macro_rules! err {
($fmt:expr, $($arg:expr),*) => {
$crate::errors::Error::TestFailed(format!($fmt, $($arg),*))
};
($fmt:expr) => { $crate::errors::Error::TestFailed(format!($fmt)) }
}
#[macro_export]
macro_rules! extel_assert {
($cond:expr) => {
match $cond {
true => $crate::pass!(),
false => $crate::fail!("[{}] assertion failed", stringify!($cond)),
}
};
($cond:expr, $err:expr) => {
match $cond {
true => $crate::pass!(),
false => $crate::fail!("{}", $err),
}
};
($cond:expr, $err_fmt:expr, $($arg:expr),+) => {
extel_assert!($cond, format!($err_fmt, $($arg),+))
}
}
#[macro_export]
macro_rules! cmd {
($cmd_str:expr) => {{
let mut cmd_str_tokens = $cmd_str.trim().split(' ');
let command = cmd_str_tokens.next().expect("no command was provided");
let mut args = cmd_str_tokens.map(String::from);
let mut final_args: Vec<String> = Vec::new();
while let Some(token) = args.next() {
let tok_chars = token.chars().collect::<Vec<_>>();
let first_char = tok_chars[0];
if ['"', '\''].contains(&first_char) {
if *tok_chars.last().unwrap() == first_char {
final_args.push(tok_chars[1..tok_chars.len()-1].into_iter().collect());
break;
}
let mut quoted_arg = vec![token.chars().skip(1).collect::<String>()];
loop {
let Some(token) = args.next() else {
break;
};
let tok_chars = token.chars().collect::<Vec<_>>();
if *tok_chars.last().unwrap() == first_char {
quoted_arg.push(
tok_chars[0..tok_chars.len()-1].into_iter().collect()
);
} else {
quoted_arg.push(token);
}
}
final_args.extend(quoted_arg);
} else {
final_args.push(token);
}
}
let mut command = ::std::process::Command::new(command);
if !final_args.is_empty() {
command.args(final_args);
}
command
}};
($cmd_str:literal, $($arg:expr),*) => {{
let fmt = format!($cmd_str, $($arg),*);
cmd!(fmt)
}};
($cmd:expr => []) => { ::std::process::Command::new($cmd) };
($cmd:expr => {}) => { ::std::process::Command::new($cmd) };
($cmd:expr => ()) => { ::std::process::Command::new($cmd) };
($cmd:expr => $args:expr) => { ::std::process::Command::new($cmd).args($args) };
}
#[macro_export]
macro_rules! init_test_suite {
($test_suite:ident) => {
init_test_suite!($test_suite,)
};
($test_suite:ident, $($test_name:expr),*) => {
#[allow(non_camel_case_types)]
pub struct $test_suite {
tests: Vec<$crate::Test>,
}
impl $crate::RunnableTestSet for $test_suite {
fn run(cfg: $crate::TestConfig) -> Vec<$crate::TestResult> {
let test_set = $test_suite { tests: $crate::__extel_init_tests!($($test_name),*) };
let mut writer: Option<Box<dyn ::std::io::Write>> = match cfg.output {
$crate::OutputDest::Stdout => Some(Box::new(::std::io::stdout())),
$crate::OutputDest::File(file_name) => {
let file_handle = ::std::fs::File::create(file_name).expect("could not open output file");
Some(Box::new(file_handle))
},
$crate::OutputDest::Buffer(buffer) => Some(Box::new(buffer)),
$crate::OutputDest::None => None
};
if let Some(w) = writer.as_mut() {
write!(w, "[{}]\n", std::any::type_name::<$test_suite>()).expect("buffer could not be written to");
}
test_set
.tests
.into_iter()
.enumerate()
.map(|(test_id, test)| {
let test_result = test.run_test();
if let Some(w) = writer.as_mut() {
$crate::output_test_result(w, &test_result, test_id + 1, cfg.colored);
}
test_result
})
.collect()
}
}
};
}
#[cfg(test)]
mod tests {
use std::{error::Error, path::Path};
use crate::{ExtelResult, OutputDest, RunnableTestSet, TestConfig};
fn always_succeed() -> ExtelResult {
pass!()
}
fn always_fail() -> ExtelResult {
fail!("this test failed?")
}
#[test]
fn init_test_suite_basic() {
init_test_suite!(BasicTestSet, always_succeed, always_fail);
let output_buffer: &mut Vec<u8> = &mut Vec::new();
BasicTestSet::run(
TestConfig::default()
.output(OutputDest::Buffer(output_buffer))
.colored(false),
);
let output = String::from_utf8_lossy(output_buffer);
assert_eq!(
output,
*"[extel::macros::tests::init_test_suite_basic::BasicTestSet]\n\t\
Test #1 (always_succeed) ... ok\n\t\
Test #2 (always_fail) ... FAILED\n\t [x] this test failed?\n"
);
}
#[test]
fn test_cmd() {
fn __test_cmd() -> ExtelResult {
let output = cmd!("echo -n \"hello world\"")
.output()
.expect("could not execute command");
let string_output =
String::from_utf8(output.stdout).expect("output contained non-UTF-8 chars");
if string_output == *"hello world" {
pass!()
} else {
fail!(
"invalid result, expected 'hello world', got '{}'",
string_output
)
}
}
init_test_suite!(__test_cmd_suite, __test_cmd);
let mut output_buffer: Vec<u8> = Vec::new();
__test_cmd_suite::run(
TestConfig::default()
.output(OutputDest::Buffer(&mut output_buffer))
.colored(false),
);
let output_result = String::from_utf8_lossy(&output_buffer);
assert_eq!(
output_result,
"[extel::macros::tests::test_cmd::__test_cmd_suite]\n\tTest #1 (__test_cmd) ... ok\n"
);
}
#[test]
fn test_cmd_fmt_arg() {
const EXPECTED: &str = "viva las vegas";
fn __test_cmd() -> ExtelResult {
let output = cmd!("echo -n \"{}\"", EXPECTED)
.output()
.expect("could not execute command");
let string_output =
String::from_utf8(output.stdout).expect("output contained non-UTF-8 chars");
if string_output == *EXPECTED {
pass!()
} else {
fail!(
"invalid result, expected '{}', got '{}'",
EXPECTED,
string_output
)
}
}
init_test_suite!(__test_cmd_suite, __test_cmd);
let mut output_buffer: Vec<u8> = Vec::new();
__test_cmd_suite::run(
TestConfig::default()
.output(OutputDest::Buffer(&mut output_buffer))
.colored(false),
);
let output_result = String::from_utf8_lossy(&output_buffer);
assert_eq!(
output_result,
"[extel::macros::tests::test_cmd_fmt_arg::__test_cmd_suite]\n\tTest #1 (__test_cmd) ... ok\n"
);
}
#[test]
fn test_extel_assert() {
const EXPECTED: &str = "viva las vegas";
fn __test_cmd() -> ExtelResult {
let output = cmd!("echo -n \"{}\"", EXPECTED)
.output()
.expect("could not execute command");
let string_output =
String::from_utf8(output.stdout).expect("output contained non-UTF-8 chars");
extel_assert!(
string_output == *EXPECTED,
"invalid result, expected '{}', got '{}'",
EXPECTED,
string_output
)
}
init_test_suite!(__test_cmd_suite, __test_cmd);
let mut output_buffer: Vec<u8> = Vec::new();
__test_cmd_suite::run(
TestConfig::default()
.output(OutputDest::Buffer(&mut output_buffer))
.colored(false),
);
let output_result = String::from_utf8_lossy(&output_buffer);
assert_eq!(
output_result,
"[extel::macros::tests::test_extel_assert::__test_cmd_suite]\n\tTest #1 (__test_cmd) ... ok\n"
);
}
#[test]
fn test_cmd_path() {
const EXPECTED: &str = "viva las vegas";
fn __test_cmd() -> ExtelResult {
let exe_path = Path::new("echo");
let output = cmd!(exe_path => ["-n", EXPECTED])
.output()
.expect("could not execute command");
let string_output =
String::from_utf8(output.stdout).expect("output contained non-UTF-8 chars");
extel_assert!(
string_output == *EXPECTED,
"invalid result, expected '{}', got '{}'",
EXPECTED,
string_output
)
}
init_test_suite!(__test_cmd_suite, __test_cmd);
let mut output_buffer: Vec<u8> = Vec::new();
__test_cmd_suite::run(
TestConfig::default()
.output(OutputDest::Buffer(&mut output_buffer))
.colored(false),
);
let output_result = String::from_utf8_lossy(&output_buffer);
assert_eq!(
output_result,
"[extel::macros::tests::test_cmd_path::__test_cmd_suite]\n\tTest #1 (__test_cmd) ... ok\n"
);
}
#[test]
fn test_cmd_question_mark_operator() {
const EXPECTED: &str = "viva las vegas";
fn __test_cmd() -> ExtelResult {
let exe_path = Path::new("echo");
let output = cmd!(exe_path => ["-n", EXPECTED]).output()?;
let string_output = String::from_utf8(output.stdout)?;
extel_assert!(
string_output == *EXPECTED,
"invalid result, expected '{}', got '{}'",
EXPECTED,
string_output
)
}
init_test_suite!(__test_cmd_suite, __test_cmd);
let mut output_buffer: Vec<u8> = Vec::new();
__test_cmd_suite::run(
TestConfig::default()
.output(OutputDest::Buffer(&mut output_buffer))
.colored(false),
);
let output_result = String::from_utf8_lossy(&output_buffer);
assert_eq!(
output_result,
"[extel::macros::tests::test_cmd_question_mark_operator::__test_cmd_suite]\n\tTest #1 (__test_cmd) ... ok\n"
);
}
#[test]
fn test_cmd_empty_arg() -> Result<(), Box<dyn Error>> {
let bracket_output = String::from_utf8(cmd!("echo" => []).output()?.stdout)?;
let brace_output = String::from_utf8(cmd!("echo" => {}).output()?.stdout)?;
let paren_output = String::from_utf8(cmd!("echo" => ()).output()?.stdout)?;
Ok(assert!(
bracket_output == brace_output && brace_output == paren_output
))
}
}