use std::sync::{Mutex, MutexGuard, PoisonError};
use crcbl_console::{Context, Registry};
use crcbl_core::log::{Filter, console};
static ORDER: Mutex<()> = Mutex::new(());
fn in_order() -> MutexGuard<'static, ()> {
ORDER.lock().unwrap_or_else(PoisonError::into_inner)
}
fn with_filter(directives: &str) -> MutexGuard<'static, ()> {
let order = in_order();
let _ = crcbl_core::log::try_init_logging(Filter::parse(directives));
assert!(
crcbl_core::log::set_filter(Filter::parse(directives)),
"this binary's logger has to be the process's for any of this to be observable",
);
order
}
fn ringed(target: &str) -> Vec<console::Record> {
console::snapshot()
.into_iter()
.filter(|record| record.target == target)
.collect()
}
#[test]
fn a_filter_installed_at_runtime_decides_the_next_record() {
let target = "crcbl_core_console_log::live_filter";
let _order = with_filter("info");
assert!(
!crcbl_core::log::__enabled(log::Level::Debug, target),
"the default level does not admit a debug record",
);
log::debug!(target: target, "before the filter moved");
assert!(
ringed(target).is_empty(),
"the facade dropped it before any sink saw it",
);
assert!(crcbl_core::log::set_filter(Filter::parse(&format!(
"info,{target}=debug"
))));
assert!(
crcbl_core::log::__enabled(log::Level::Debug, target),
"the directive admits it now",
);
log::debug!(target: target, "after the filter moved");
assert_eq!(
ringed(target)
.iter()
.map(|record| record.message.clone())
.collect::<Vec<_>>(),
["after the filter moved"],
"the widened filter is what let the record through",
);
}
fn registry() -> Registry {
Registry::gather(&[crcbl_core::console_table()]).expect("no two entries claim one name")
}
#[test]
fn the_log_command_installs_the_filter_it_names() {
let _order = with_filter("info");
let registry = registry();
let mut host = ();
let mut cx = Context::new(®istry, &mut host);
registry
.execute(&mut cx, "log crcbl_render=debug")
.expect("a readable directive");
assert_eq!(cx.lines(), ["log info,crcbl_render=debug"]);
let installed = crcbl_core::log::filter().expect("the logger is installed");
assert_eq!(installed.to_string(), "info,crcbl_render=debug");
assert_eq!(installed.level_for("crcbl_render"), log::LevelFilter::Debug);
assert_eq!(installed.level_for("crcbl_core"), log::LevelFilter::Info);
}
#[test]
fn the_log_command_bare_prints_the_filter_in_force() {
let _order = with_filter("warn,crcbl_vk=trace");
let registry = registry();
let mut host = ();
let mut cx = Context::new(®istry, &mut host);
registry.execute(&mut cx, "log").expect("`log` reads");
assert_eq!(cx.lines(), ["log warn,crcbl_vk=trace"]);
assert_eq!(
crcbl_core::log::filter().expect("installed").to_string(),
"warn,crcbl_vk=trace",
"reading it must not have changed it",
);
}
#[test]
fn a_bad_directive_is_refused_and_changes_nothing() {
let _order = with_filter("info");
let registry = registry();
let mut host = ();
let mut cx = Context::new(®istry, &mut host);
let fault = registry
.execute(&mut cx, "log crcbl_vk=louder")
.expect_err("`louder` is not a level");
assert!(
fault.message().contains("crcbl_vk=louder"),
"the fault has to name the directive it refused: {fault}",
);
assert!(cx.lines().is_empty(), "{:?}", cx.lines());
assert_eq!(
crcbl_core::log::filter().expect("installed").to_string(),
"info",
"the filter is what it was",
);
}
#[test]
fn a_console_line_reaches_the_capture_and_the_ring_under_one_target() {
let _order = with_filter("info");
let message = "antialiasing = cmaa2";
let logs = crcbl_core::log::capture();
console::print(message);
let records = logs.records();
let mine: Vec<_> = records
.iter()
.filter(|record| record.message == message)
.collect();
assert_eq!(mine.len(), 1, "{records:?}");
assert_eq!(mine[0].target, console::CONSOLE_TARGET);
assert_eq!(mine[0].level, log::Level::Info);
let ringed: Vec<_> = ringed(console::CONSOLE_TARGET)
.into_iter()
.filter(|record| record.message == message)
.collect();
assert_eq!(ringed.len(), 1, "the same line is in the ring: {ringed:?}");
assert_eq!(ringed[0].level, log::Level::Info);
}