Printing additional information on panic

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.

You can get around this with AssertUnwindSafe.

Thank you, it seems to be working!
The new macro become:

macro_rules! create_test {
    ($name:ident {
        $(call $function:ident ($($arg:expr),*);)*
    }) => {
        #[test]
        fn $name() {
            let mut interpreter = Interpreter::default();

            $(
                match std::panic::catch_unwind(std::panic::AssertUnwindSafe(
                    || { 
                        $function(&mut interpreter $(, $arg)*); 
                    }
                )) {
                    Ok(()) => (),
                    Err(_) => {
                        panic!(
                            "Expression caused a panic:  \"{}\"",
                            stringify!(call $function($($arg),*))
                        );
                    }
                }
            )*
        }
    };
}

Which gives the output:

---- test_1 stdout ----
thread 'test_1' panicked at 'assertion failed: `(left == right)`
  left: `1`,
 right: `0`'
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'test_1' panicked at 'Expression caused a panic: "call foo (1)"'

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.