rust_loguru/
test_utils.rs1use crate::level::LogLevel;
6use crate::record::Record;
7use std::sync::{Arc, Mutex};
8
9#[derive(Debug, Clone)]
11pub struct CapturedRecord {
12 pub level: LogLevel,
13 pub message: String,
14 pub module: Option<String>,
15 pub file: Option<String>,
16 pub line: Option<u32>,
17}
18
19#[derive(Clone, Default)]
21pub struct LogCapture {
22 records: Arc<Mutex<Vec<CapturedRecord>>>,
23}
24
25impl LogCapture {
26 pub fn new() -> Self {
27 Self {
28 records: Arc::new(Mutex::new(Vec::new())),
29 }
30 }
31
32 pub fn handle(&self, record: &Record) {
33 let captured = CapturedRecord {
34 level: record.level(),
35 message: record.message().to_string(),
36 module: Some(record.module().to_string()),
37 file: Some(record.file().to_string()),
38 line: Some(record.line()),
39 };
40 self.records.lock().unwrap().push(captured);
41 }
42
43 pub fn clear(&self) {
44 self.records.lock().unwrap().clear();
45 }
46
47 pub fn records(&self) -> Vec<CapturedRecord> {
48 self.records.lock().unwrap().clone()
49 }
50
51 pub fn contains_message(&self, msg: &str) -> bool {
52 self.records().iter().any(|r| r.message.contains(msg))
53 }
54
55 pub fn contains_level(&self, level: LogLevel) -> bool {
56 self.records().iter().any(|r| r.level == level)
57 }
58}
59
60pub struct MockLogger {
62 pub capture: LogCapture,
63 pub level: LogLevel,
64}
65
66impl MockLogger {
67 pub fn new(level: LogLevel) -> Self {
68 Self {
69 capture: LogCapture::new(),
70 level,
71 }
72 }
73
74 pub fn log(&self, record: &Record) -> bool {
75 if record.level() >= self.level {
76 self.capture.handle(record);
77 true
78 } else {
79 false
80 }
81 }
82
83 pub fn level(&self) -> LogLevel {
84 self.level
85 }
86
87 pub fn set_level(&mut self, level: LogLevel) {
88 self.level = level;
89 }
90}
91
92#[macro_export]
94macro_rules! assert_log_contains {
95 ($capture:expr, $msg:expr) => {
96 assert!(
97 $capture.contains_message($msg),
98 "Expected log to contain message: {}\nCaptured: {:?}",
99 $msg,
100 $capture.records()
101 );
102 };
103}
104
105#[macro_export]
107macro_rules! assert_log_level {
108 ($capture:expr, $level:expr) => {
109 assert!(
110 $capture.contains_level($level),
111 "Expected log to contain level: {:?}\nCaptured: {:?}",
112 $level,
113 $capture.records()
114 );
115 };
116}
117
118pub struct TempLogLevel<'a, L: ?Sized> {
120 logger: &'a mut L,
121 old_level: LogLevel,
122 set_level: fn(&mut L, LogLevel),
123}
124
125impl<'a, L: ?Sized> TempLogLevel<'a, L> {
126 pub fn new(
127 logger: &'a mut L,
128 new_level: LogLevel,
129 set_level: fn(&mut L, LogLevel),
130 get_level: fn(&L) -> LogLevel,
131 ) -> Self {
132 let old_level = get_level(logger);
133 set_level(logger, new_level);
134 Self {
135 logger,
136 old_level,
137 set_level,
138 }
139 }
140}
141
142impl<L: ?Sized> Drop for TempLogLevel<'_, L> {
143 fn drop(&mut self) {
144 (self.set_level)(self.logger, self.old_level);
145 }
146}