1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#![cfg(test)]
//! Crate-local names for the shared helpers in `leviath-testkit`, plus the ones
//! that only this crate needs.
pub(crate) use leviath_testkit::{PANIC_HOOK_LOCK, with_silenced_panics, with_tracing};
/// Silence the process panic hook for as long as the guard lives.
///
/// `with_silenced_panics` takes a synchronous closure, which is enough when the
/// panic happens inside it. The lane tests provoke a panic on a *tokio worker*,
/// some time after the spawning call returns, so the hook has to stay swapped
/// across an await - hence a guard rather than a closure.
pub(crate) struct SilentPanics {
/// Held for the guard's lifetime: swapping the process-global hook has to be
/// serialized against every other test that does it.
_lock: std::sync::MutexGuard<'static, ()>,
/// The hook to put back.
previous: PanicHook,
}
/// What `std::panic::take_hook` hands back, named so the field above doesn't
/// trip `clippy::type_complexity`.
type PanicHook = Box<dyn Fn(&std::panic::PanicHookInfo<'_>) + Sync + Send + 'static>;
/// The hook installed while the guard lives: print nothing. A named function
/// rather than two `|_| {}` closures so the silencer and the throwaway below are
/// the same (exercised) code.
fn swallow_panic(_: &std::panic::PanicHookInfo<'_>) {}
impl SilentPanics {
pub(crate) fn install() -> Self {
let lock = PANIC_HOOK_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(swallow_panic));
Self {
_lock: lock,
previous,
}
}
}
impl Drop for SilentPanics {
fn drop(&mut self) {
// Swap a throwaway in so the real hook can be moved out of `&mut self`.
// (An `Option` + `take` would work too, but its `None` arm can never be
// reached, and the coverage gate counts that.)
let previous = std::mem::replace(&mut self.previous, Box::new(swallow_panic));
std::panic::set_hook(previous);
}
}
/// The global end of the system-prompt hint cascade for a spawn under test,
/// where only the batch-tool hint is ever varied. The shell hint follows it so
/// the two never disagree in a test that only cares about one of them; a test
/// that does care builds the struct itself.
pub(crate) fn hints(on: bool) -> leviath_core::config::PromptHints {
leviath_core::config::PromptHints {
batch_tool: on,
shell: on,
}
}