can_motor_control/transport/
mock.rs1use std::collections::VecDeque;
4use std::os::fd::RawFd;
5use std::sync::{Arc, Mutex};
6
7use motor_codec::{BusCapabilities, CanFrame};
8
9use super::{CanBus, TransportError};
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14pub enum MockRecordedCall {
15 Send,
17 Drain,
19}
20
21#[derive(Default)]
22struct Shared {
23 sent: Vec<CanFrame>,
24 pending_inbound: VecDeque<CanFrame>,
25 recorded: Vec<MockRecordedCall>,
26}
27
28#[derive(Clone)]
35pub struct MockCanBus {
36 name: String,
37 caps: BusCapabilities,
38 me: Arc<Mutex<Shared>>,
39 peer: Option<Arc<Mutex<Shared>>>,
40}
41
42impl MockCanBus {
43 pub fn new(name: impl Into<String>) -> Self {
45 Self::with_capabilities(name, BusCapabilities::classical())
46 }
47
48 pub fn new_fd(name: impl Into<String>) -> Self {
51 Self::with_capabilities(name, BusCapabilities::fd())
52 }
53
54 pub fn with_capabilities(name: impl Into<String>, caps: BusCapabilities) -> Self {
56 Self {
57 name: name.into(),
58 caps,
59 me: Arc::new(Mutex::new(Shared::default())),
60 peer: None,
61 }
62 }
63
64 pub fn pair(name_a: impl Into<String>, name_b: impl Into<String>) -> (Self, Self) {
66 let a_shared = Arc::new(Mutex::new(Shared::default()));
67 let b_shared = Arc::new(Mutex::new(Shared::default()));
68 let caps = BusCapabilities::classical();
69 let a = Self {
70 name: name_a.into(),
71 caps,
72 me: a_shared.clone(),
73 peer: Some(b_shared.clone()),
74 };
75 let b = Self {
76 name: name_b.into(),
77 caps,
78 me: b_shared,
79 peer: Some(a_shared),
80 };
81 (a, b)
82 }
83
84 pub fn sent_frames(&self) -> Vec<CanFrame> {
86 self.me.lock().unwrap().sent.clone()
87 }
88
89 pub fn recorded_calls(&self) -> Vec<MockRecordedCall> {
91 self.me.lock().unwrap().recorded.clone()
92 }
93
94 pub fn inject_frame(&self, frame: CanFrame) {
96 self.me.lock().unwrap().pending_inbound.push_back(frame);
97 }
98}
99
100impl CanBus for MockCanBus {
101 fn name(&self) -> &str {
102 &self.name
103 }
104
105 fn capabilities(&self) -> BusCapabilities {
106 self.caps
107 }
108
109 fn send(&mut self, frame: &CanFrame) -> Result<(), TransportError> {
110 if frame.is_fd() && !self.caps.supports_fd {
112 return Err(TransportError::FdFrameOnNonFdBus);
113 }
114 if frame.len > self.caps.max_payload_len {
115 return Err(TransportError::PayloadExceedsBusCapacity {
116 len: frame.len,
117 max: self.caps.max_payload_len,
118 });
119 }
120 {
121 let mut me = self.me.lock().unwrap();
122 me.sent.push(*frame);
123 me.recorded.push(MockRecordedCall::Send);
124 }
125 match &self.peer {
127 Some(p) => p.lock().unwrap().pending_inbound.push_back(*frame),
128 None => self.me.lock().unwrap().pending_inbound.push_back(*frame),
129 }
130 Ok(())
131 }
132
133 fn drain_inbound_nonblocking(&mut self) -> Result<Vec<CanFrame>, TransportError> {
134 let mut me = self.me.lock().unwrap();
135 me.recorded.push(MockRecordedCall::Drain);
136 Ok(me.pending_inbound.drain(..).collect())
137 }
138
139 fn raw_fd(&self) -> Option<RawFd> {
140 None
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn pair_loopback_delivers() {
150 let (mut a, mut b) = MockCanBus::pair("a", "b");
151 let f = CanFrame::classical(0x101, &[1, 2, 3]).unwrap();
152 a.send(&f).unwrap();
153 let got = b.drain_inbound_nonblocking().unwrap();
154 assert_eq!(got, vec![f]);
155 assert!(a.drain_inbound_nonblocking().unwrap().is_empty());
157 }
158
159 #[test]
160 fn self_loopback_delivers() {
161 let mut bus = MockCanBus::new("solo");
162 let f = CanFrame::classical(0x101, &[0xFF; 8]).unwrap();
163 bus.send(&f).unwrap();
164 let got = bus.drain_inbound_nonblocking().unwrap();
165 assert_eq!(got, vec![f]);
166 }
167
168 #[test]
169 fn inject_and_drain() {
170 let bus = MockCanBus::new("solo");
171 let f = CanFrame::classical(0x10, &[1; 8]).unwrap();
172 bus.inject_frame(f);
173 let mut bus = bus;
174 assert_eq!(bus.drain_inbound_nonblocking().unwrap(), vec![f]);
175 }
176
177 #[test]
178 fn sent_frames_records_in_order() {
179 let mut bus = MockCanBus::new("solo");
180 let f1 = CanFrame::classical(0x10, &[1; 8]).unwrap();
181 let f2 = CanFrame::classical(0x11, &[2; 8]).unwrap();
182 bus.send(&f1).unwrap();
183 bus.send(&f2).unwrap();
184 assert_eq!(bus.sent_frames(), vec![f1, f2]);
185 }
186
187 #[test]
188 fn fd_frame_rejected_on_classical_mock() {
189 let mut bus = MockCanBus::new("solo");
190 let f = CanFrame::fd(0x100, &[0; 16]).unwrap();
191 assert!(matches!(
192 bus.send(&f),
193 Err(TransportError::FdFrameOnNonFdBus)
194 ));
195 }
196
197 #[test]
198 fn fd_mock_accepts_fd_frames() {
199 let mut bus = MockCanBus::with_capabilities("fd", BusCapabilities::fd());
200 let f = CanFrame::fd(0x100, &[0; 16]).unwrap();
201 bus.send(&f).unwrap();
202 assert_eq!(bus.drain_inbound_nonblocking().unwrap(), vec![f]);
203 }
204
205 #[test]
206 fn new_fd_round_trips_fd_frame_preserving_flag_and_payload() {
207 let mut bus = MockCanBus::new_fd("fd0");
208 assert!(bus.capabilities().supports_fd);
209 let f = CanFrame::fd(0x123, &[0xAB; 24]).unwrap();
210 bus.send(&f).unwrap();
211 let got = bus.drain_inbound_nonblocking().unwrap();
212 assert_eq!(got.len(), 1);
213 assert!(got[0].is_fd());
214 assert_eq!(got[0].payload(), &[0xAB; 24]);
215 }
216
217 #[test]
218 fn recorded_calls_track_send_vs_drain() {
219 let mut bus = MockCanBus::new("solo");
220 let _ = bus.send(&CanFrame::classical(0x10, &[]).unwrap());
221 let _ = bus.drain_inbound_nonblocking();
222 let _ = bus.send(&CanFrame::classical(0x11, &[]).unwrap());
223 assert_eq!(
224 bus.recorded_calls(),
225 vec![
226 MockRecordedCall::Send,
227 MockRecordedCall::Drain,
228 MockRecordedCall::Send
229 ]
230 );
231 }
232
233 #[test]
234 fn raw_fd_is_none() {
235 let bus = MockCanBus::new("solo");
236 assert!(bus.raw_fd().is_none());
237 }
238}