xref: /aosp_15_r20/external/crosvm/base/tests/syslog.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #![allow(clippy::field_reassign_with_default)]
6 
7 use std::io;
8 use std::io::Write;
9 use std::sync::Arc;
10 
11 use base::syslog::test_only_ensure_inited;
12 use base::syslog::LogArgs;
13 use base::syslog::LogConfig;
14 use base::syslog::Priority;
15 use base::syslog::State;
16 use base::syslog::Syslogger;
17 use env_logger::fmt;
18 use log::Level;
19 use log::Log;
20 use log::Record;
21 use sync::Mutex;
22 
23 #[derive(Clone)]
24 struct MockWrite {
25     buffer: Arc<Mutex<Vec<u8>>>,
26 }
27 
28 impl MockWrite {
new() -> Self29     fn new() -> Self {
30         Self {
31             buffer: Arc::new(Mutex::new(vec![])),
32         }
33     }
34 
into_inner(self) -> Vec<u8>35     fn into_inner(self) -> Vec<u8> {
36         Arc::try_unwrap(self.buffer).unwrap().into_inner()
37     }
38 }
39 
40 impl Write for MockWrite {
write(&mut self, buf: &[u8]) -> io::Result<usize>41     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
42         self.buffer.lock().write(buf)
43     }
44 
flush(&mut self) -> io::Result<()>45     fn flush(&mut self) -> io::Result<()> {
46         Ok(())
47     }
48 }
49 
50 #[test]
syslog_log()51 fn syslog_log() {
52     let state = State::default();
53     state.log(
54         &log::RecordBuilder::new()
55             .level(Level::Error)
56             .file(Some(file!()))
57             .line(Some(line!()))
58             .args(format_args!("hello syslog"))
59             .build(),
60     );
61 }
62 
63 #[test]
proc_name()64 fn proc_name() {
65     let state = State::new(LogConfig {
66         log_args: LogArgs {
67             proc_name: String::from("syslog-test"),
68             ..Default::default()
69         },
70         ..Default::default()
71     })
72     .unwrap();
73     state.log(
74         &log::RecordBuilder::new()
75             .level(Level::Error)
76             .file(Some(file!()))
77             .line(Some(line!()))
78             .args(format_args!("hello syslog"))
79             .build(),
80     );
81 }
82 
83 #[test]
macros()84 fn macros() {
85     test_only_ensure_inited().unwrap();
86     log::error!("this is an error {}", 3);
87     log::warn!("this is a warning {}", "uh oh");
88     log::info!("this is info {}", true);
89     log::debug!("this is debug info {:?}", Some("helpful stuff"));
90 }
91 
pipe_formatter(buf: &mut fmt::Formatter, record: &Record<'_>) -> io::Result<()>92 fn pipe_formatter(buf: &mut fmt::Formatter, record: &Record<'_>) -> io::Result<()> {
93     writeln!(buf, "{}", record.args())
94 }
95 
96 #[test]
syslogger_char()97 fn syslogger_char() {
98     let output = MockWrite::new();
99     let mut cfg = LogConfig::default();
100     cfg.pipe_formatter = Some(Box::new(pipe_formatter));
101     cfg.pipe = Some(Box::new(output.clone()));
102     let state = Mutex::new(State::new(cfg).unwrap());
103 
104     let mut syslogger = Syslogger::test_only_from_state(Level::Info, || state.lock());
105 
106     let string = "chars";
107     for c in string.chars() {
108         syslogger.write_all(&[c as u8]).expect("error writing char");
109     }
110 
111     syslogger
112         .write_all(b"\n")
113         .expect("error writing newline char");
114 
115     std::mem::drop(syslogger);
116     std::mem::drop(state);
117     assert_eq!(
118         format!("{}\n", string),
119         String::from_utf8_lossy(&output.into_inner()[..])
120     );
121 }
122 
123 #[test]
syslogger_line()124 fn syslogger_line() {
125     let output = MockWrite::new();
126     let mut cfg = LogConfig::default();
127     cfg.pipe_formatter = Some(Box::new(pipe_formatter));
128     cfg.pipe = Some(Box::new(output.clone()));
129     let state = Mutex::new(State::new(cfg).unwrap());
130 
131     let mut syslogger = Syslogger::test_only_from_state(Level::Info, || state.lock());
132 
133     let s = "Writing string to syslog\n";
134     syslogger
135         .write_all(s.as_bytes())
136         .expect("error writing string");
137 
138     std::mem::drop(syslogger);
139     std::mem::drop(state);
140     assert_eq!(s, String::from_utf8_lossy(&output.into_inner()[..]));
141 }
142 
143 #[test]
syslogger_partial()144 fn syslogger_partial() {
145     let output = MockWrite::new();
146     let state = Mutex::new(
147         State::new(LogConfig {
148             pipe: Some(Box::new(output.clone())),
149             ..Default::default()
150         })
151         .unwrap(),
152     );
153 
154     let mut syslogger = Syslogger::test_only_from_state(Level::Info, || state.lock());
155 
156     let s = "Writing partial string";
157     // Should not log because there is no newline character
158     syslogger
159         .write_all(s.as_bytes())
160         .expect("error writing string");
161 
162     std::mem::drop(syslogger);
163     std::mem::drop(state);
164     assert_eq!(Vec::<u8>::new(), output.into_inner());
165 }
166 
167 #[test]
log_priority_try_from_number()168 fn log_priority_try_from_number() {
169     assert_eq!("0".try_into(), Ok(Priority::Emergency));
170     assert!(Priority::try_from("100").is_err());
171 }
172 
173 #[test]
log_priority_try_from_words()174 fn log_priority_try_from_words() {
175     assert_eq!("EMERGENCY".try_into(), Ok(Priority::Emergency));
176     assert!(Priority::try_from("_EMERGENCY").is_err());
177 }
178 
179 #[test]
log_should_always_be_enabled_for_level_show_all()180 fn log_should_always_be_enabled_for_level_show_all() {
181     let state = State::new(LogConfig {
182         log_args: LogArgs {
183             filter: String::from("trace"),
184             ..Default::default()
185         },
186         ..Default::default()
187     })
188     .unwrap();
189 
190     assert!(state.enabled(
191         log::RecordBuilder::new()
192             .level(Level::Debug)
193             .build()
194             .metadata(),
195     ));
196 }
197 
198 #[test]
log_should_always_be_disabled_for_level_silent()199 fn log_should_always_be_disabled_for_level_silent() {
200     let state = State::new(LogConfig {
201         log_args: LogArgs {
202             filter: String::from("off"),
203             ..Default::default()
204         },
205         ..Default::default()
206     })
207     .unwrap();
208 
209     assert!(!state.enabled(
210         log::RecordBuilder::new()
211             .level(Level::Debug)
212             .build()
213             .metadata(),
214     ));
215 }
216 
217 #[test]
log_should_be_enabled_if_filter_level_has_a_lower_or_equal_priority()218 fn log_should_be_enabled_if_filter_level_has_a_lower_or_equal_priority() {
219     let state = State::new(LogConfig {
220         log_args: LogArgs {
221             filter: String::from("info"),
222             ..Default::default()
223         },
224         ..Default::default()
225     })
226     .unwrap();
227 
228     assert!(state.enabled(
229         log::RecordBuilder::new()
230             .level(Level::Info)
231             .build()
232             .metadata(),
233     ));
234     assert!(state.enabled(
235         log::RecordBuilder::new()
236             .level(Level::Warn)
237             .build()
238             .metadata(),
239     ));
240 }
241 
242 #[test]
log_should_be_disabled_if_filter_level_has_a_higher_priority()243 fn log_should_be_disabled_if_filter_level_has_a_higher_priority() {
244     let state = State::new(LogConfig {
245         log_args: LogArgs {
246             filter: String::from("info"),
247             ..Default::default()
248         },
249         ..Default::default()
250     })
251     .unwrap();
252 
253     assert!(!state.enabled(
254         log::RecordBuilder::new()
255             .level(Level::Debug)
256             .build()
257             .metadata(),
258     ));
259 }
260 
261 #[test]
path_overides_should_apply_to_logs()262 fn path_overides_should_apply_to_logs() {
263     let state = State::new(LogConfig {
264         log_args: LogArgs {
265             filter: String::from("info,test=debug"),
266             ..Default::default()
267         },
268         ..Default::default()
269     })
270     .unwrap();
271 
272     assert!(!state.enabled(
273         log::RecordBuilder::new()
274             .level(Level::Debug)
275             .build()
276             .metadata(),
277     ));
278     assert!(state.enabled(
279         log::RecordBuilder::new()
280             .level(Level::Debug)
281             .target("test")
282             .build()
283             .metadata(),
284     ));
285 }
286 
287 #[test]
longest_path_prefix_match_should_apply_if_multiple_filters_match()288 fn longest_path_prefix_match_should_apply_if_multiple_filters_match() {
289     let state = State::new(LogConfig {
290         log_args: LogArgs {
291             filter: String::from("info,test=debug,test::silence=off"),
292             ..Default::default()
293         },
294         ..Default::default()
295     })
296     .unwrap();
297 
298     assert!(!state.enabled(
299         log::RecordBuilder::new()
300             .level(Level::Debug)
301             .build()
302             .metadata(),
303     ));
304 
305     assert!(state.enabled(
306         log::RecordBuilder::new()
307             .level(Level::Debug)
308             .target("test")
309             .build()
310             .metadata(),
311     ));
312     assert!(!state.enabled(
313         log::RecordBuilder::new()
314             .level(Level::Error)
315             .target("test::silence")
316             .build()
317             .metadata(),
318     ));
319 }
320