// file: crates/ksp-logging-lib/tests/span_lifecycle.rs // version: 1 //! Integration tests for formatted KSP span lifecycle timing output. use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import const TEST_TARGET: &str = "ksp-logging-lib"; #[derive(Clone)] struct SharedWriter { buffer: std::sync::Arc>>, } impl SharedWriter { fn new(buffer: std::sync::Arc>>) -> Self { return Self { buffer }; } } impl std::io::Write for SharedWriter { fn write(&mut self, bytes: &[u8]) -> std::io::Result { let lock_result = self.buffer.lock(); let mut buffer = match lock_result { std::result::Result::Ok(buffer) => buffer, std::result::Result::Err(_) => return std::result::Result::Err(std::io::Error::other("span test buffer is poisoned")), }; buffer.extend_from_slice(bytes); return std::result::Result::Ok(bytes.len()); } fn flush(&mut self) -> std::io::Result<()> { return std::result::Result::Ok(()); } } fn captured_text(buffer: &std::sync::Arc>>) -> std::string::String { let lock_result = buffer.lock(); let bytes = match lock_result { std::result::Result::Ok(bytes) => bytes.clone(), std::result::Result::Err(error) => error.into_inner().clone(), }; return std::string::String::from_utf8_lossy(bytes.as_slice()).into_owned(); } #[test] fn new_and_close_span_events_expose_busy_and_idle_timing_fields() { let buffer = std::sync::Arc::new(std::sync::Mutex::new(std::vec::Vec::new())); let writer_buffer = std::sync::Arc::clone(&buffer); let layer = tracing_subscriber::fmt::layer() .with_writer(move || -> SharedWriter { return SharedWriter::new(std::sync::Arc::clone(&writer_buffer)); }) .with_ansi(false) .with_target(true) .with_span_events(tracing_subscriber::fmt::format::FmtSpan::NEW | tracing_subscriber::fmt::format::FmtSpan::CLOSE); let subscriber = tracing_subscriber::registry().with(layer); tracing::subscriber::with_default(subscriber, || { let span = ksp_logging_lib::trace_span!(target: TEST_TARGET, "timed_scope", domain = "logging"); span.in_scope(|| { std::hint::black_box(42_u32); return; }); drop(span); return; }); let text = captured_text(&buffer); assert!(text.contains("timed_scope")); assert!(text.contains("new")); assert!(text.contains("close")); assert!(text.contains("time.busy")); assert!(text.contains("time.idle")); }