I recently stared experimenting with writing tests using a DSL with the help of macro_rules, and it was a much smother experience than I expected. But I encountered some trouble when I wanted to add more information in the case the test panics.
Here is a simplified version of the macro I use:
macro_rules! create_test {
($name:ident {
$(call $function:ident ($($arg:expr),*);)*
}) => {
#[test]
fn $name() {
let mut interpreter = Interpreter::default();
$(
$function(&mut interpreter $(, $arg)*);
)*
}
};
}
With the usage of the macro looking like this:
#[derive(Debug, Default)]
struct Interpreter(String);
fn foo(_: &mut Interpreter, num: i32) {
assert_eq!(num, 0);
}
create_test!(
test_1 {
call foo(0);
call foo(1);
}
);
The current output when you run the test is:
---- test_1 stdout ----
panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `0`'
But I would like to change it so that the current "expression" is also printed:
---- test_1 stdout ----
Expression failed:
call foo (1)
panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `0`'
I've tried to wrap the expression in std::panic::catch_unwind
but with no luck:
macro_rules! create_test {
($name:ident {
$(call $function:ident ($($arg:expr),*);)*
}) => {
#[test]
fn $name() {
let mut interpreter = Interpreter::default();
$(
// Error: &mut Interpreter` may not be safely transferred across an unwind boundary
match std::panic::catch_unwind(|| { $function(&mut interpreter $(, $arg)*); }) {
Ok(()) => (),
Err(e) => {
panic!(
"Expression failed:\n\t{}\n{:?}",
stringify!(call $function(&mut interpreter $(, $arg)*)),
e
);
}
}
)*
}
};
}
I've also experimenting with std::panic::set_hook
which looks to give the output I want, until you run the tests in parallel, and encounter race conditions about what the current expression is.