gts_logger/logbackend/
threadmock.rs1use crate::error::GtsLoggerError;
2use crate::logbackend::LogBackend;
3use core::fmt::Debug;
4use gts_transport::error::GtsTransportError;
5use gts_transport::membackend::memchunk::MemChunkHolder;
6use gts_transport::sync::lfringspsc::{spsc_ring_pair, SpScRingData, SpScRingSender};
7use std::cell::UnsafeCell;
8use std::sync::mpsc::Receiver;
9use std::sync::Mutex;
10use std::sync::{mpsc, Arc};
11use std::time::Duration;
12
13pub struct LogContext {}
14
15pub struct MockThreadLogBacked<const RSIZE: usize, T>
16where
17 T: Copy + Send,
18{
19 queue_rx: Receiver<T>,
20 run_flag: Arc<Mutex<bool>>,
21 join_handle: Option<std::thread::JoinHandle<()>>,
22 log_tx: UnsafeCell<SpScRingSender<RSIZE, T, MemChunkHolder<SpScRingData<RSIZE, T>>>>,
23}
24
25impl<T, const RSIZE: usize> MockThreadLogBacked<RSIZE, T>
26where
27 T: Copy + Send + 'static + Debug,
28{
29 pub fn new() -> Self {
30 let flag = Arc::new(Mutex::new(false));
31 let flag_clone = flag.clone();
34 let (queue_tx, queue_rx) = mpsc::channel();
36 let (log_tx, mut log_rx) = spsc_ring_pair::<RSIZE, T, _>(MemChunkHolder::zeroed());
37
38 let join_handle = Some(std::thread::spawn(move || {
39 let queue_tx = queue_tx;
40 while !*flag_clone.lock().unwrap() {
41 match log_rx.try_recv() {
42 Ok(res) => {
43 queue_tx.send(*res).unwrap();
44 }
45 Err(GtsTransportError::WouldBlock) => {}
46 _ => unreachable!(),
47 }
48 std::thread::sleep(Duration::from_millis(100));
49 }
50 }));
51
52 MockThreadLogBacked {
53 queue_rx,
54 run_flag: flag,
55 join_handle,
56 log_tx: log_tx.into(),
57 }
58 }
59
60 pub fn pop_front(&self) -> Option<T> {
61 self.queue_rx.try_recv().ok()
62 }
63}
64impl<T, const RSIZE: usize> Drop for MockThreadLogBacked<RSIZE, T>
65where
66 T: Copy + Send,
67{
68 fn drop(&mut self) {
69 *self.run_flag.lock().unwrap() = true;
70 self.join_handle.take().unwrap().join().unwrap();
71 }
72}
73
74impl<T, const RSIZE: usize> LogBackend<T> for MockThreadLogBacked<RSIZE, T>
75where
76 T: Copy + Send,
77{
78 fn log(&self, event: T) -> Result<(), GtsLoggerError> {
79 let log_tx = unsafe { &mut *self.log_tx.get() };
84 log_tx.send(&event).unwrap();
85 Ok(())
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use crate::logbackend::threadmock::MockThreadLogBacked;
92 use crate::logclient::LogClient;
93 use arrayvec::ArrayString;
94 use serde::{Deserialize, Serialize};
95 use std::time::Duration;
96
97 #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
98 pub struct LogOneStruct {
99 some_num: u64,
100 some_other_num: u64,
101 some_string: ArrayString<16>,
102 }
103
104 #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
105 pub struct LogTwoStruct {
106 some_string: ArrayString<16>,
107 }
108
109 #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
110 #[serde(tag = "t", content = "c")]
111 pub enum LogEvent {
112 LogOneOne(LogOneStruct),
113 LogTwo(LogTwoStruct),
114 }
115
116 #[test]
117 fn create_logger() {
118 let event = LogEvent::LogOneOne(LogOneStruct {
119 some_num: 5,
120 some_other_num: 7,
121 some_string: ArrayString::from("333").unwrap(),
122 });
123
124 let copy_event = event;
125
126 let log_client = LogClient::<_, LogEvent>::new(MockThreadLogBacked::<3000, _>::new());
127
128 log_client.log(event).unwrap();
129
130 std::thread::sleep(Duration::from_millis(200));
131 let rr = log_client.backend().pop_front();
132 assert!(matches!(rr, Some(ev) if ev.data == copy_event));
133
134 std::thread::sleep(Duration::from_millis(200));
135 let rr = log_client.backend().pop_front();
136 assert!(rr.is_none());
137
138 log_client.log(event).unwrap();
139 std::thread::sleep(Duration::from_millis(200));
140 let rr = log_client.backend().pop_front();
141 assert!(matches!(rr, Some(ev) if ev.data == copy_event));
142
143 std::thread::sleep(Duration::from_millis(200));
144 let rr = log_client.backend().pop_front();
145 assert!(rr.is_none());
146 }
147}