5个版本
使用旧的Rust 2015
0.1.4 | 2018年4月14日 |
---|---|
0.1.3 | 2017年12月1日 |
0.1.2 | 2017年11月29日 |
0.1.1 | 2017年11月28日 |
0.1.0 | 2017年11月28日 |
#565 in 测试
每月294次下载
用于sarek
37KB
358 行
使用动态类型检查控制Rust panic
panic_control
crate提供工具,用于在有意引发的panic中测试代码行为,同时区分预期和意外的panic,以便能够捕获断言失败等。
许可证
许可协议为以下之一
- Apache许可证2.0版本 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
您可选择。
贡献
除非您明确声明,否则根据Apache-2.0许可证定义,您提交的任何有意包含在本作品中的贡献,均应双许可,如上所述,没有任何附加条款或条件。
lib.rs
:
使用动态类型检查控制panic
有时需要测试Rust代码在panic发生时的行为。可以在测试生成的线程中故意引发panic,并在线程连接后观察其效果。问题在于,区分“良性的”panic和表示实际错误的panic(如断言失败)可能很麻烦。
另一个问题是默认panic hook的行为。它对于获取意外线程panic的原因信息非常有用,但对于故意引发panic的测试,它会产生讨厌的输出噪声。panic hook可以被覆盖,但自定义panic hook会影响整个程序,在典型用法中是测试运行器;很容易误用它们,导致重要的错误信息未报告。
将子线程中发生的panic传播到其父线程的最简单方法,由标准库提供,是在JoinHandle::join
的结果上调用unwrap
。遗憾的是,由于Any
实现中存在一个问题,导致最终的panic消息没有从子线程的panic中传递信息。
此crate提供了一些工具和用户友好的接口,通过动态类型检查来区分预期panic和非预期panic,以在可控和输出友好的方式测试panic。
预期panic类型
将panic指定为预期的推荐方法是使用自定义类型的值作为panic!
的参数。这个类型可以是简单的单元-like结构体,也可以携带来自panic点的额外信息。任何panic值类型都应该是Sized
、'static
和Send
。为了使值可用于测试,它还应该实现至少Debug
和PartialEq
。
示例
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"));