fluent_asserter/
panic_asserter.rs1use super::*;
2use std::panic;
3use std::sync::{Arc, Mutex};
4lazy_static! {
10 static ref LOCK_FOR_PANIC_ASSERTER: std::sync::Mutex<()> = Mutex::new(());
11}
12
13pub struct PanicAssertions {
14 actual_panic_message: String,
15}
16
17impl PanicAssertions {
18 pub fn new(actual_panic_message: String) -> PanicAssertions {
19 PanicAssertions {
20 actual_panic_message,
21 }
22 }
23
24 pub fn with_message(self, expected_panic_message: &str) {
25 if self.actual_panic_message != expected_panic_message {
26 panic!(
27 "Expected a panic message '{}', but found '{}'",
28 expected_panic_message, self.actual_panic_message
29 )
30 }
31 }
32
33 pub fn with_having_message(self, expected_panic_message: &str) {
34 if !self.actual_panic_message.contains(expected_panic_message) {
35 panic!(
36 "The text '{}' is not present in the panic message '{}'",
37 expected_panic_message, self.actual_panic_message
38 )
39 }
40 }
41}
42
43impl<F, R> PanicAsserter<F, R>
44where
45 F: FnOnce() -> R + panic::UnwindSafe,
46{
47 pub fn new(f: F) -> Self {
48 PanicAsserter { value: f }
49 }
50
51 pub fn panics(self) -> PanicAssertions {
52 let _guard = LOCK_FOR_PANIC_ASSERTER.lock();
54 let old_hook = panic::take_hook();
55 let global_buffer = Arc::new(Mutex::new(String::new()));
56
57 register_panic_hook_to_capture_output(&global_buffer);
58 let result = panic::catch_unwind(self.value);
59 panic::set_hook(old_hook);
60
61 PanicAssertions {
62 actual_panic_message: get_panic_message_if_present(result, global_buffer),
63 }
64 }
65
66 pub fn does_not_panic(self) {
67 let result = self.catch_unwind_silent();
68
69 if result.is_err() {
70 panic!("Expected code to panic, but it does not panic."); }
72 }
73
74 fn catch_unwind_silent(self) -> std::thread::Result<R> {
75 let old_hook = panic::take_hook();
76 panic::set_hook(Box::new(|_| {}));
77 let result = panic::catch_unwind(self.value);
78 panic::set_hook(old_hook);
79 result
80 }
81}
82
83fn register_panic_hook_to_capture_output(global_buffer: &Arc<Mutex<String>>) {
84 panic::set_hook({
85 let global_buffer = global_buffer.clone();
86 Box::new(move |info| {
87 let mut global_buffer = global_buffer.lock().unwrap();
88
89 if let Some(s) = info.payload().downcast_ref::<&str>() {
91 global_buffer.push_str(s);
92 }
93
94 if let Some(s) = info.payload().downcast_ref::<String>() {
96 global_buffer.push_str(s);
97 }
98 })
99 });
100}
101
102fn get_panic_message_if_present<R>(
103 result: Result<R, Box<dyn std::any::Any + Send>>,
104 global_buffer: Arc<Mutex<String>>,
105) -> String {
106 match result {
107 Ok(_res) => {
108 panic!("There was no panic, but it was expected.")
109 }
110 Err(_) => {
111 return global_buffer.lock().unwrap().to_string();
112 }
113 }
114}