Can I resume panics on different threads or how to capture test output

⚓ Rust    📅 2025-10-13    👤 surdeus    👁️ 2      

surdeus

Currently test runtime in Rust spawns a new thread for every single test.
In my case this is problematic, since I am writing tests to a C++ library, which really doesn't want to be reinitialized on other threads.

So instead I use LazyStatic to spawn a single thread to which Rust tests will be sending tasks to:

pub static WORKER: LazyLock<Mutex<Worker>> = LazyLock::new(|| {
    let (task_tx, task_rx) = mpsc::channel();

    std::thread::spawn(move || {
        while let Ok(Task { task, result_tx }) = task_rx.recv() {
            let env = unsafe { environment::TestEnvironment::build() };
            let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
              env.run(task);
            }));
            std::mem::drop(env);
            _ = result_tx.send(result);
        }
    });

    Mutex::new(Worker { task_tx })
});

The problem is that there are tests which can panic.
To deal with this I call resume_unwind inside tests:

let worker = loop {
    match worker::WORKER.lock() {
        Ok(guard) => break guard,
        Err(_) => worker::WORKER.clear_poison(),
    }
};

let (result_tx, result_rx) = mpsc::channel();
worker
    .task_tx
    .send(worker::Task {
        task: Box::new(task),
        result_tx,
    })
    .expect("Worker thread cannot fail");

let result = result_rx.recv().expect("Worker thread cannot fail");
std::mem::drop(worker);
match result {
    Ok(result) => result,
    Err(panic) => std::panic::resume_unwind(panic),
}

This works, however, if the test fails for whatever reason the output is empty:

#[lib::test]
fn example_test() {
    panic!("Explicitly fail")
}
running 1 test
test example_test... FAILED

failures:

failures:
    example_test

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.90s

If I pass --nocapture to tests I can see the actual error message, but this is bad UX:

running 1 test
thread '<unnamed>' panicked at tests\test_utils.rs:113:7:
Explicitly fail
test example_test... FAILED

failures:

failures:
    example_test

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.90s

Anything I can do to make test runtime capture this output?

Instead of resume_unwind I can extract the string out of the Box<dyn Any> and panic with it, but this looses the error location.

2 posts - 1 participant

Read full topic

🏷️ Rust_feed