use termint::prelude::Color;
use termint::style::Stylize;
#[macro_export]
macro_rules! assert_all {
( $($cond:expr),+) => {
{
let mut errors = 0;
$(
mark_run(stringify!($cond));
let _ = match $cond {
true => mark_ok(),
false => {
errors += 1;
mark_fail(stringify!($cond));
}
};
)*
if errors != 0 {
panic!("{} assertion(s) failed.", errors);
}
}
};
}
#[macro_export]
macro_rules! assert_all_match_result {
( $($cond:expr),+) => {
{
let mut errors = 0;
$(
mark_run(stringify!($cond));
let _ = match $cond {
Ok(_) => mark_ok(),
Err(e) => {
errors += 1;
mark_fail(stringify!($cond));
eprintln!("Error: '{}' ", e.to_string());
}
};
)*
if errors != 0 {
panic!("{} assertion(s) failed.", errors);
}
}
}
}
pub fn mark_run(message: &str) {
"====================="
.chars()
.for_each(|_| print!("{}", "=".bg(Color::Gray).fg(Color::Gray)));
print!(" > Run: '{}'", message);
}
pub fn mark_ok() {
print!(": 👍🏼 ");
"====================="
.chars()
.for_each(|_| print!("{}", "=".bg(Color::Green).fg(Color::Green)));
println!();
}
pub fn mark_fail(message: &str) {
eprint!(": 👎🏼 ");
"====================="
.chars()
.for_each(|_| print!("{}", "=".bg(Color::Red).fg(Color::Red)));
eprint!(" Assertion failed for: '{}'", message);
println!();
}
#[macro_export]
macro_rules! assert_match_option {
($current:expr) => {
assert_match_option!($current, "Assertion failed")
};
($current:expr, $msg:literal) => {
match $current {
Some(_) => Ok(()),
None => Err($msg),
}
};
}
#[macro_export]
macro_rules! assert_not_match_option {
($current:expr) => {
assert_not_match_option!($current, "Assertion failed")
};
($current:expr, $msg:literal) => {
match $current {
Some(_) => Err($msg),
None => Ok(()),
}
};
}
#[macro_export]
macro_rules! assert_match_result {
($current:expr) => {
assert_match_result!($current, "Assertion failed")
};
($current:expr, $msg:literal) => {
match $current {
Ok(_) => Ok(()),
Err(_) => Err($msg),
}
};
}
#[macro_export]
macro_rules! assert_match_natural_result {
($current:expr) => {
match $current {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
};
}
#[macro_export]
macro_rules! assert_not_match_result {
($current:expr) => {
assert_not_match_result!($current, "Assertion failed")
};
($current:expr, $msg:literal) => {
match $current {
Ok(_) => Err($msg),
Err(_) => Ok(()),
}
};
}
#[macro_export]
macro_rules! assert_match_values {
($current:expr, $ok_value:pat, $nok_value:pat) => {
assert_match_values!($current, $ok_value, $nok_value, "Assertion failed")
};
($current:expr, $ok_value:pat, $nok_value:pat, $msg:literal) => {
assert!(
facility_match_values!($current, $ok_value, $nok_value).is_ok(),
$msg
);
};
}
#[macro_export]
macro_rules! assert_not_match_values {
($current:expr, $nok_value:pat, $ok_value:pat) => {
assert_not_match_values!($current, $nok_value, $ok_value, "Assertion failed")
};
($current:expr, $nok_value:pat, $ok_value:pat, $msg:literal) => {
assert!(
facility_match_values!($current, $nok_value, $ok_value).is_err(),
$msg
);
};
}
#[macro_export]
macro_rules! assert_none {
($value:expr) => {
assert_none!($value, "Assert none failed")
};
($value:expr, $msg:literal) => {
assert_eq!(None, $value, $msg)
};
}
#[macro_export]
macro_rules! assert_not_none {
($value:expr) => {
assert_not_none!($value, "Assertion not none failed")
};
($value:expr, $msg:literal) => {
assert_ne!(None, $value, $msg)
};
}
#[macro_export]
macro_rules! assert_slices_eq {
($awaiting:expr, $result:expr) => {
assert_slices_eq!($awaiting, $result, "Assertion failed");
};
($awaiting:expr, $result:expr, $msg:literal) => {{
assert_eq!($awaiting.len(), $result.len(), $msg);
let mut cpt = 0;
while cpt < $awaiting.len() {
assert_eq!($awaiting[cpt], $result[cpt], $msg);
cpt += 1;
}
}};
}
#[macro_export]
macro_rules! assert_slices_ne {
($awaiting:expr, $result:expr) => {
assert_slices_ne!($awaiting, $result, "Assertion failed");
};
($awaiting:expr, $result:expr, $msg:literal) => {{
let mut cpt = 0;
let min = match $awaiting.len() < $result.len() {
true => $awaiting.len(),
false => $result.len(),
};
while cpt < min && ($awaiting[cpt] == $result[cpt]) {
cpt += 1;
}
assert!(!((cpt == min) & ($awaiting.len() == $result.len())), $msg);
}};
}
#[macro_export]
macro_rules! assert_same_slices {
($awaiting:expr, $result:expr) => {
assert_same_slices!($awaiting, $result, "Assertion failed");
};
($awaiting:expr, $result:expr, $msg:literal) => {{
assert_eq!($awaiting.len(), $result.len(), $msg);
let mut cpt = 0;
let mut is_same = true;
while cpt < $awaiting.len() && is_same {
is_same = $result.iter().filter(|&x| *x == $awaiting[cpt]).count() != 0;
cpt += 1;
}
assert!(is_same, $msg);
}};
}
#[macro_export]
macro_rules! assert_not_same_slices {
($awaiting:expr, $result:expr) => {
assert_not_same_slices!($awaiting, $result, "Assertion failed")
};
($awaiting:expr, $result:expr, $msg:literal) => {{
let min = match $awaiting.len() < $result.len() {
true => $awaiting.len(),
false => $result.len(),
};
let mut common = 1;
let mut cpt = 0;
while cpt < min && common != 0 {
common = $result.iter().filter(|&x| *x == $awaiting[cpt]).count();
cpt += 1;
}
assert!((common == 0) | ($awaiting.len() != $result.len()), $msg);
}};
}
#[macro_export]
macro_rules! facility_match_values {
($current:expr, $ok_value:pat, $nok_value:pat) => {
facility_match_values!($current, $ok_value, $nok_value, "Assertion failed")
};
($current:expr, $ok_value:pat, $nok_value:pat, $msg:literal) => {
match $current {
$ok_value => Ok(()),
$nok_value => Err($msg),
}
};
}
#[macro_export]
macro_rules! fail {
() => {
fail!("Test failed")
};
($msg:literal) => {
panic!("{}", $msg)
};
}
pub fn random_string(length: usize) -> String {
use rand::prelude::*;
let mut rng = rand::rng();
let mut chars: Vec<char> = Vec::new();
repeat!(length, {
chars.push(rng.sample(rand::distr::Alphanumeric) as char);
});
String::from_iter(chars)
}
#[macro_export]
macro_rules! repeat {
($nb:expr, $b:block) => {
let mut max: usize = $nb as usize;
while max != 0 {
{
$b
}
max -= 1;
}
};
}
#[macro_export]
macro_rules! setup {
($b:block) => {{
use std::thread;
let t = thread::spawn(|| $b);
t.join().unwrap();
}};
}
#[macro_export]
macro_rules! teardown {
($b:block) => {
struct AutoDown;
impl Drop for AutoDown {
fn drop(&mut self) {
$b
}
}
let _a = AutoDown;
};
}
#[macro_export]
macro_rules! title {
($text:literal) => {{
use termint::enums::Wrap;
use termint::prelude::Color;
use termint::style::Stylize;
let message = format!(" {} ", $text.fg(Color::Magenta).wrap(Wrap::Letter).bold());
println!("[ {:_^70} ]", message);
}};
($value:expr) => {{
use termint::enums::Wrap;
use termint::prelude::Color;
use termint::style::Stylize;
let message = format!(" {} ", $value.fg(Color::Magenta).wrap(Wrap::Letter).bold());
println!("[ {:_^70} ]", message);
}};
}
#[cfg(test)]
mod test {
use crate::mark_fail;
use crate::mark_ok;
use crate::mark_run;
use crate::random_string;
use std::fs;
use std::fs::File;
use std::path::Path;
#[test]
fn test_assert_all_true() {
assert_all!(true);
}
#[should_panic]
#[test]
fn test_assert_all_false_panic() {
assert_all!(false);
}
#[should_panic]
#[test]
fn test_assert_all_false_true_panic() {
assert_all!(false, true);
}
#[should_panic]
#[test]
fn test_assert_all_true_false_panic() {
assert_all!(true, false);
}
#[test]
fn test_assert_all_multi() {
assert_all!(true, 1 + 1 == 2);
}
#[should_panic]
#[test]
fn test_assert_all_multi_panic() {
assert_all!(true, 1 + 1 == 2, 1 * 2 == 6);
}
#[should_panic]
#[test]
fn test_assert_multi_long_panic() {
assert_all!(true, 1 + 1 == 2, 1 * 2 == 6, 0 * 0 == 0, 'a' == 'b');
}
#[should_panic]
#[test]
fn test_assert_all_compte_panic() {
assert_all!(compute(), 1 + 1 == 2, 1 * 2 == 6);
}
fn compute() -> bool {
false
}
#[test]
fn test_assert_all_match_result() {
title!("Assert all match result");
assert_all_match_result!(result_ok1(), result_ok2())
}
fn result_ok1() -> Result<(), &'static str> {
Ok(())
}
fn result_ok2() -> Result<(), &'static str> {
Ok(())
}
#[should_panic]
#[test]
fn test_assert_all_match_result_fail() {
assert_all_match_result!(result_ok1(), result_fail1(), result_ok2(), result_fail2())
}
fn result_fail1() -> Result<(), &'static str> {
Err("result fail1")
}
fn result_fail2() -> Result<(), &'static str> {
Err("result fail2")
}
#[test]
fn test_assert_all_match_result_with_arguments() {
["value", "Value", "VALUE"]
.iter()
.for_each(|v| assert_all_match_result!(arg_with_value1(v), arg_with_value2(v)));
}
fn arg_with_value1(val: &str) -> Result<(), &'static str> {
match val.to_ascii_lowercase().as_str() {
"value" => Ok(()),
_ => Err("Invalid value"),
}
}
fn arg_with_value2(val: &str) -> Result<(), &'static str> {
match val.to_ascii_lowercase().as_str() {
"value" => Ok(()),
_ => Err("Invalid value"),
}
}
#[should_panic]
#[test]
fn test_assert_all_match_result_with_invalid_arguments() {
["value", "VALUE", "pas value"]
.iter()
.for_each(|v| assert_all_match_result!(arg_with_value1("value"), arg_with_value2(v)));
}
#[should_panic]
#[test]
fn test_fail() {
fail!();
}
#[test]
fn test_setup_teardown() {
setup!({
File::create(Path::new("test.txt")).unwrap();
});
teardown!({
fs::remove_file(Path::new("test.txt")).unwrap();
});
assert!(Path::new("test.txt").exists());
}
#[test]
fn test_setup_teardown_file_is_not_exist() {
setup!({
let _ = fs::remove_file(Path::new("test2.txt"));
});
teardown!({
let _ = fs::remove_file(Path::new("test2.txt"));
});
assert!(!Path::new("test2.txt").exists());
}
#[test]
fn test_assert_match_option() -> Result<(), &'static str> {
assert_match_option!(Some(5))
}
#[test]
fn test_assert_match_none() {
let result = assert_match_option!(None::<i32>);
assert_eq!(result, Err("Assertion failed"));
}
#[test]
fn test_assert_match_with_specific_error_message() {
let result = assert_match_option!(None::<i32>, "It's failed");
assert_eq!(result, Err("It's failed"));
}
#[test]
fn test_assert_not_match_option() {
let result = assert_not_match_option!(None::<i32>);
assert_eq!(result, Ok(()));
}
#[test]
fn test_assert_not_match_not_none() {
let result = assert_not_match_option!(Some(5));
assert_eq!(result, Err("Assertion failed"));
}
#[test]
fn test_assert_not_match_none_with_message() {
let result = assert_not_match_option!(Some("Foo"), "Foo failed");
assert_eq!(result, Err("Foo failed"));
}
#[test]
fn test_assert_match_result() -> Result<(), &'static str> {
assert_match_result!(Ok::<i32, &str>(5))
}
#[test]
fn test_assert_match_result_with_message() {
let result = assert_match_result!(Err::<(), &str>("Foo"), "It's failed");
assert_eq!(result, Err("It's failed"));
}
#[test]
fn test_assert_match_natural_result() -> Result<(), &'static str> {
assert_match_natural_result!(match_natural_result(true))
}
fn match_natural_result(must_ok: bool) -> Result<&'static str, &'static str> {
match must_ok {
true => Ok("Ok"),
false => Err("Error value"),
}
}
#[test]
fn test_assert_match_natural_result_with_failed() {
assert!(assert_match_natural_result!(match_natural_result(false)).is_err());
}
#[test]
fn test_assert_match_natural_result_other_type() -> Result<(), i32> {
assert_match_natural_result!(match_result_with_i32(true))
}
fn match_result_with_i32(must_ok: bool) -> Result<(), i32> {
match must_ok {
true => Ok(()),
false => Err(1),
}
}
#[test]
fn test_assert_match_natural_result_other_type_failed() {
assert!(assert_match_natural_result!(match_result_with_i32(false)).is_err());
}
#[test]
fn test_assert_not_match_result() -> Result<(), &'static str> {
assert_not_match_result!(Err::<(), i32>(5), "It's failed")
}
#[test]
fn test_assert_none_with_method() {
assert_none!(none_method());
}
fn none_method() -> Option<i32> {
None
}
#[should_panic]
#[test]
fn test_assert_none_with_none() {
assert_none!(Some(3));
}
#[test]
fn test_assert_not_none_valid() {
assert_not_none!(Some(1));
}
#[should_panic]
#[test]
fn test_assert_not_none_from_method() {
assert_not_none!(none_method());
}
#[test]
fn test_assert_slices_eq() {
let slice1 = [1, 2, 3, 4, 5];
let slice2 = [1, 2, 3, 4, 5];
assert_slices_eq!(slice1, slice2);
}
#[should_panic]
#[test]
fn test_assert_slices_eq_not_same_size() {
let slice1 = ['a', 'b', 'c', 'd'];
let slice2 = ['a', 'b', 'c'];
assert_slices_eq!(slice1, slice2);
}
#[should_panic]
#[test]
fn test_assert_slices_eq_not_equal() {
let slice1 = [1, 2, 3, 4, 5];
let slice2 = [1, 2, 3, 5, 4];
assert_slices_eq!(slice1, slice2);
}
#[test]
fn test_assert_slices_eq_direct() {
assert_slices_eq!([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]);
}
#[test]
fn test_assert_slices_eq_with_vector() {
let vec1 = vec![1, 2, 3, 4, 5];
let vec2 = vec![1, 2, 3, 4, 5];
assert_slices_eq!(vec1, vec2);
}
#[test]
fn test_assert_slices_eq_with_string() {
assert_slices_eq!("abcdef".as_bytes(), "abcdef".as_bytes());
}
#[should_panic]
#[test]
fn test_assert_slices_eq_with_message() {
assert_slices_eq!([1, 2, 3, 4], [1, 2, 3, 3], "Error with this test");
}
#[test]
fn test_assert_slices_ne_not_same_content() {
assert_slices_ne!([1, 2, 3, 4, 5], [1, 2, 3, 4, 0]);
}
#[test]
fn test_assert_slices_ne_not_same_size() {
assert_slices_ne!([1, 2, 3, 4], [1, 2, 3]);
}
#[test]
fn test_assert_slices_ne_not_same_size_reversed() {
assert_slices_ne!([1, 2, 3], [1, 2, 3, 4]);
}
#[should_panic]
#[test]
fn test_assert_slices_ne_same_slices() {
assert_slices_ne!([1, 2, 3, 4], [1, 2, 3, 4]);
}
#[should_panic]
#[test]
fn test_assert_slices_ne_with_message() {
assert_slices_ne!([1, 2, 3, 4], [1, 2, 3, 4], "Slices are not valids");
}
#[test]
fn test_assert_match_values() {
assert_match_values!(true, true, false);
}
#[should_panic]
#[test]
fn test_assert_match_values_not_match() {
assert_match_values!(false, true, false);
}
#[test]
fn test_assert_not_match_values() {
assert_not_match_values!(true, false, true);
}
#[should_panic]
#[test]
fn test_assert_not_match_values_not_match() {
assert_not_match_values!(true, true, false);
}
#[test]
fn test_assert_match_values_with_chars() {
assert_match_values!('a', 'a'..'z', _);
}
#[test]
fn test_assert_not_match_values_with_chars() {
assert_not_match_values!(' ', 'a'..'z', _);
}
#[test]
fn test_assert_same_slices_same_order_and_size() {
assert_same_slices!([1, 2, 4, 5], [1, 2, 4, 5]);
}
#[test]
fn test_assert_same_slices_same_reverse_order() {
assert_same_slices!([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]);
}
#[should_panic]
#[test]
fn test_assert_same_slices_same_order_not_size() {
assert_same_slices!([1, 2, 3, 4], [1, 2, 3, 4, 5]);
}
#[should_panic]
#[test]
fn test_assert_same_slices_same_reverse_order_and_size() {
assert_same_slices!([5, 4, 3, 2, 1], [5, 4, 3]);
}
#[test]
fn test_assert_same_slices_same_vector() {
let v1 = vec!['a', 'b', 'c', 'd'];
let v2 = vec!['d', 'c', 'b', 'a'];
assert_same_slices!(v1, v2);
}
#[should_panic]
#[test]
fn test_assert_same_slices_same_message() {
let v1 = vec!['a', 'b', 'c', 'd'];
let v2 = vec!['e', 'f', 'b', 'a'];
assert_same_slices!(v1, v2, "Slices are not the same");
}
#[test]
fn test_assert_not_same_slices_same_size() {
assert_not_same_slices!([1, 2, 3, 4, 5], [6, 7, 8, 9, 10]);
}
#[test]
fn test_assert_not_same_slices_not_same_size() {
assert_not_same_slices!([1, 2, 3], [1, 2, 3, 4]);
}
#[should_panic]
#[test]
fn test_assert_not_same_slices_with_same_slices() {
assert_not_same_slices!([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]);
}
#[test]
fn test_assert_not_same_slices_same_size_not_same_content() {
assert_not_same_slices!([1, 2, 3, 4, 5], [1, 2, 3, 4, 4]);
}
#[test]
fn test_repeat_10_repeats() {
let mut counter = 0;
repeat!(10, {
counter += 1;
});
assert_eq!(10, counter);
}
#[test]
fn test_repeat_zero_repeat() {
let mut counter = 1;
repeat!(0, {
counter += 1;
});
assert_eq!(1, counter);
}
#[test]
fn test_random_string() {
let string = random_string(10);
assert!(!string.is_empty());
}
#[test]
fn test_random_string_verify_length() {
let string = random_string(10);
title!(string.clone());
assert_eq!(10, string.len());
}
#[test]
fn test_random_string_twice_differents() {
repeat!(20, {
let string = random_string(10);
let string2 = random_string(10);
assert_ne!(string, string2);
});
}
#[test]
fn test_random_string_length0() {
let string = random_string(0);
assert!(string.is_empty());
}
}