5 releases
Uses old Rust 2015
0.1.4 | Apr 14, 2018 |
---|---|
0.1.3 | Dec 1, 2017 |
0.1.2 | Nov 29, 2017 |
0.1.1 | Nov 28, 2017 |
0.1.0 | Nov 28, 2017 |
#565 in Testing
294 downloads per month
Used in sarek
37KB
358 lines
Controlled Rust Panics Using Dynamic Type Checking
The panic_control
crate provides utilities to test code behaviors in
intentionally caused panics, while discriminating between the expected
and unexpected panics so as to be able to catch assertion failures
and the like.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
lib.rs
:
Controlled panics using dynamic type checking.
Sometimes there is a need to test how Rust code behaves on occurrence of a panic. A panic can be invoked on purpose in a thread spawned by the test and the effects observed after the thread is joined. The problem with "benign" panics is that it may be cumbersome to tell them apart from panics indicating actual errors, such as assertion failures.
Another issue is the behavior of the default panic hook. It is very useful for getting information about the cause of an unexpected thread panic, but for tests causing panics on purpose it produces annoying output noise. The panic hook can be overridden, but custom panic hooks affect the entire program, which in typical usage is the test runner; it is easy to misuse them causing important error information to go unreported.
The simplest way, as provided by the standard library, to propagate
a panic that occurred in a child thread to the thread that spawned it
is to call unwrap
on the result of JoinHandle::join
. Unfortunately,
due to an issue with
the implementation of Any
, the resulting panic message does not relay
information from the child thread's panic.
This crate provides utilities and an ergonomic interface for testing panics in a controlled and output-friendly way using dynamic type checks to discern between expected and unexpected panics.
Expected Panic Type
The recommended way to designate panics as expected is by using values of
a custom type as the parameter for panic!
. The type could be as simple
as a token unit-like struct, or it can be equipped to carry additional
information from the panic site.
Any panic value type shall be Sized
, 'static
, and Send
.
For the value to be usable in testing, it should also implement
at least Debug
and PartialEq
.
Examples
use panic_control::{Context, Outcome};
use panic_control::{chain_hook_ignoring, spawn_quiet};
use panic_control::ThreadResultExt;
use std::thread;
#[derive(Debug, PartialEq, Eq)]
enum Expected {
Token,
Int(i32),
String(String)
}
// Rust's stock test runner does not provide a way to do global
// initialization and the tests are run in parallel in a random
// order by default. So this is our solution, to be called at
// the beginning of every test exercising a panic with an
// Expected value.
fn silence_expected_panics() {
use std::sync::{Once, ONCE_INIT};
static HOOK_ONCE: Once = ONCE_INIT;
HOOK_ONCE.call_once(|| {
chain_hook_ignoring::<Expected>()
});
}
// ...
silence_expected_panics();
let thread_builder = thread::Builder::new()
.name("My panicky thread".into());
let ctx = Context::<Expected>::from(thread_builder);
let h = ctx.spawn(|| {
let unwind_me = TypeUnderTest::new();
assert!(unwind_me.doing_fine());
// ^-- If this fails, join() will return Err
panic!(Expected::String("Rainbows and unicorns!".into()));
});
let outcome = h.join().unwrap_or_propagate();
match outcome {
Outcome::Panicked(Expected::String(s)) => {
println!("thread panicked as expected: {}", s);
}
_ => panic!("unexpected value returned from join()")
}
let ctx = Context::<Expected>::new();
let h = ctx.spawn_quiet(|| {
let h = spawn_quiet(|| {
panic!("Sup dawg, we heard you like panics \
so we put a panic in your panic!");
});
h.join().unwrap_or_propagate();
});
let res = h.join();
let msg = res.panic_value_as_str().unwrap();
assert!(msg.contains("panic in your panic"));