1use crate::*;
6use serde::Deserialize;
7use starnix_uapi::errors::Errno;
8use std::collections::HashMap;
9
10#[derive(Deserialize, Debug)]
11struct Scenario {
12 name: String,
13 initial_termios: TermiosConfig,
14 events: Vec<Event>,
15 #[allow(dead_code)]
16 final_termios: TermiosConfig,
17}
18
19#[derive(Deserialize, Debug)]
20struct TermiosConfig {
21 #[serde(default)]
22 c_iflag: Vec<String>,
23 #[serde(default)]
24 c_oflag: Vec<String>,
25 #[serde(default)]
26 c_lflag: Vec<String>,
27 #[allow(dead_code)]
28 c_cflag: Option<u32>, c_cc: Option<HashMap<String, u8>>,
30}
31
32#[derive(Deserialize, Debug)]
33#[serde(untagged)]
34enum TraceData {
35 Bytes(Vec<u8>),
36 String(String),
37}
38
39impl TraceData {
40 fn to_bytes(&self) -> Vec<u8> {
41 match self {
42 TraceData::Bytes(b) => b.clone(),
43 TraceData::String(s) => s.as_bytes().to_vec(),
44 }
45 }
46}
47
48#[derive(Deserialize, Debug)]
49#[serde(tag = "type")]
50enum Event {
51 #[serde(rename = "write_to_master")]
52 WriteToMaster { data: TraceData },
53 #[serde(rename = "read_from_master")]
54 ReadFromMaster { data: TraceData },
55 #[serde(rename = "read_from_slave")]
56 ReadFromSlave { data: TraceData },
57 #[serde(rename = "write_to_slave")]
58 WriteToSlave { data: TraceData },
59 #[serde(rename = "write_to_slave_blocked")]
60 WriteToSlaveBlocked { data: TraceData },
61 #[serde(rename = "write_to_slave_unexpected_success")]
62 WriteToSlaveUnexpectedSuccess { data: TraceData },
63 #[serde(rename = "set_packet_mode")]
64 SetPacketMode { enabled: bool },
65 #[serde(rename = "set_termios")]
66 SetTermios { termios: TermiosConfig },
67 #[serde(rename = "flush")]
68 Flush { side: String, queue_selector: String },
69 #[serde(rename = "wait_until_readable")]
70 WaitUntilReadable { side: String },
71}
72
73struct TestBuffer {
74 data: Vec<u8>,
75}
76
77impl TestBuffer {
78 fn new(data: Vec<u8>) -> Self {
79 Self { data }
80 }
81}
82
83impl InputBuffer for TestBuffer {
84 fn available(&self) -> usize {
85 self.data.len()
86 }
87 fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno> {
88 if size > self.data.len() {
89 return error!(EAGAIN);
90 }
91 let result = self.data.drain(0..size).collect();
92 Ok(result)
93 }
94}
95
96struct TestOutputBuffer {
97 data: Vec<u8>,
98}
99
100impl TestOutputBuffer {
101 fn new() -> Self {
102 Self { data: vec![] }
103 }
104}
105
106impl OutputBuffer for TestOutputBuffer {
107 fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
108 self.data.extend_from_slice(data);
109 Ok(data.len())
110 }
111}
112
113fn parse_flags(flags: &[String], mapping: &[(u32, &str)]) -> u32 {
114 let mut result = 0;
115 for flag in flags {
116 if let Some((val, _)) = mapping.iter().find(|(_, name)| name == flag) {
117 result |= val;
118 } else {
119 panic!("Unknown flag {}", flag);
120 }
121 }
122 result
123}
124
125fn get_iflag_mapping() -> Vec<(u32, &'static str)> {
126 vec![
127 (starnix_uapi::IGNBRK, "IGNBRK"),
128 (starnix_uapi::BRKINT, "BRKINT"),
129 (starnix_uapi::IGNPAR, "IGNPAR"),
130 (starnix_uapi::PARMRK, "PARMRK"),
131 (starnix_uapi::INPCK, "INPCK"),
132 (starnix_uapi::ISTRIP, "ISTRIP"),
133 (starnix_uapi::INLCR, "INLCR"),
134 (starnix_uapi::IGNCR, "IGNCR"),
135 (starnix_uapi::ICRNL, "ICRNL"),
136 (starnix_uapi::IUCLC, "IUCLC"),
137 (starnix_uapi::IXON, "IXON"),
138 (starnix_uapi::IXANY, "IXANY"),
139 (starnix_uapi::IXOFF, "IXOFF"),
140 (starnix_uapi::IMAXBEL, "IMAXBEL"),
141 (starnix_uapi::IUTF8, "IUTF8"),
142 ]
143}
144
145fn get_oflag_mapping() -> Vec<(u32, &'static str)> {
146 vec![
147 (starnix_uapi::OPOST, "OPOST"),
148 (starnix_uapi::OLCUC, "OLCUC"),
149 (starnix_uapi::ONLCR, "ONLCR"),
150 (starnix_uapi::OCRNL, "OCRNL"),
151 (starnix_uapi::ONOCR, "ONOCR"),
152 (starnix_uapi::ONLRET, "ONLRET"),
153 (starnix_uapi::OFILL, "OFILL"),
154 (starnix_uapi::OFDEL, "OFDEL"),
155 (starnix_uapi::XTABS, "XTABS"),
156 ]
157}
158
159fn get_lflag_mapping() -> Vec<(u32, &'static str)> {
160 vec![
161 (starnix_uapi::ISIG, "ISIG"),
162 (starnix_uapi::ICANON, "ICANON"),
163 (starnix_uapi::XCASE, "XCASE"),
164 (starnix_uapi::ECHO, "ECHO"),
165 (starnix_uapi::ECHOE, "ECHOE"),
166 (starnix_uapi::ECHOK, "ECHOK"),
167 (starnix_uapi::ECHONL, "ECHONL"),
168 (starnix_uapi::ECHOCTL, "ECHOCTL"),
169 (starnix_uapi::ECHOPRT, "ECHOPRT"),
170 (starnix_uapi::ECHOKE, "ECHOKE"),
171 (starnix_uapi::FLUSHO, "FLUSHO"),
172 (starnix_uapi::NOFLSH, "NOFLSH"),
173 (starnix_uapi::TOSTOP, "TOSTOP"),
174 (starnix_uapi::PENDIN, "PENDIN"),
175 (starnix_uapi::IEXTEN, "IEXTEN"),
176 ]
177}
178
179fn get_cc_mapping() -> HashMap<&'static str, usize> {
180 let mut m = HashMap::new();
181 m.insert("VMIN", starnix_uapi::VMIN as usize);
182 m.insert("VTIME", starnix_uapi::VTIME as usize);
183 m.insert("VINTR", starnix_uapi::VINTR as usize);
184 m.insert("VQUIT", starnix_uapi::VQUIT as usize);
185 m.insert("VERASE", starnix_uapi::VERASE as usize);
186 m.insert("VKILL", starnix_uapi::VKILL as usize);
187 m.insert("VEOF", starnix_uapi::VEOF as usize);
188 m.insert("VSTART", starnix_uapi::VSTART as usize);
189 m.insert("VSTOP", starnix_uapi::VSTOP as usize);
190 m.insert("VSUSP", starnix_uapi::VSUSP as usize);
191 m.insert("VEOL", starnix_uapi::VEOL as usize);
192 m.insert("VREPRINT", starnix_uapi::VREPRINT as usize);
193 m.insert("VDISCARD", starnix_uapi::VDISCARD as usize);
194 m.insert("VWERASE", starnix_uapi::VWERASE as usize);
195 m.insert("VLNEXT", starnix_uapi::VLNEXT as usize);
196 m.insert("VEOL2", starnix_uapi::VEOL2 as usize);
197 m
198}
199
200pub fn test_replay_trace(name: &str, json_data: &str) {
201 println!("Running trace: {}", name);
202 let scenario: Scenario = serde_json::from_str(json_data).unwrap_or_else(|e| {
203 panic!("Failed to parse trace {}: {}", name, e);
204 });
205 run_scenario(scenario);
206}
207
208fn run_scenario(scenario: Scenario) {
209 let iflags = get_iflag_mapping();
210 let oflags = get_oflag_mapping();
211 let lflags = get_lflag_mapping();
212
213 let mut ld = LineDiscipline::default();
214 ld.main_open();
215 ld.replica_open();
216
217 let mut termios = crate::get_default_termios();
219 termios.c_iflag = parse_flags(&scenario.initial_termios.c_iflag, &iflags);
220 termios.c_oflag = parse_flags(&scenario.initial_termios.c_oflag, &oflags);
221 termios.c_lflag = parse_flags(&scenario.initial_termios.c_lflag, &lflags);
222
223 if let Some(cc) = &scenario.initial_termios.c_cc {
224 let mapping = get_cc_mapping();
225 for (name, &val) in cc {
226 if let Some(&idx) = mapping.get(name.as_str()) {
227 if idx < termios.c_cc.len() {
228 termios.c_cc[idx] = val;
229 }
230 } else {
231 panic!("Unknown c_cc name {}", name);
233 }
234 }
235 }
236
237 let _ = ld.set_termios(termios);
238
239 for event in scenario.events {
240 match event {
241 Event::WriteToMaster { data } => {
242 let mut buffer = TestBuffer::new(data.to_bytes());
243 let _ = ld.main_write(&mut buffer).expect("main_write failed");
244 }
245 Event::ReadFromMaster { data } => {
246 let mut buffer = TestOutputBuffer::new();
247 loop {
248 match ld.main_read(&mut buffer) {
249 Ok(_) => {}
250 Err(e) if e == (error!(EAGAIN) as Result<(), Errno>).unwrap_err() => {
251 break;
252 }
253 Err(e) => panic!("main_read failed: {:?}", e),
254 }
255 }
256 assert_eq!(
257 String::from_utf8_lossy(&buffer.data),
258 String::from_utf8_lossy(&data.to_bytes()),
259 "ReadFromMaster mismatch in {}",
260 scenario.name
261 );
262 }
263 Event::ReadFromSlave { data } => {
264 let mut buffer = TestOutputBuffer::new();
265 loop {
266 match ld.replica_read(&mut buffer) {
267 Ok(_) => {}
268 Err(e) if e == (error!(EAGAIN) as Result<(), Errno>).unwrap_err() => {
269 break;
270 }
271 Err(e) => panic!("replica_read failed: {:?}", e),
272 }
273 }
274 assert_eq!(
275 String::from_utf8_lossy(&buffer.data),
276 String::from_utf8_lossy(&data.to_bytes()),
277 "ReadFromSlave mismatch in {}",
278 scenario.name
279 );
280 }
281 Event::WriteToSlave { data } => {
282 let mut buffer = TestBuffer::new(data.to_bytes());
283 let _ = ld.replica_write(&mut buffer).expect("replica_write failed");
284 }
285 Event::WriteToSlaveBlocked { data } => {
286 let mut buffer = TestBuffer::new(data.to_bytes());
287 let result = ld.replica_write(&mut buffer);
288 assert!(
289 result.is_err(),
290 "Expected replica_write to block/fail in {}, but it succeeded",
291 scenario.name
292 );
293 assert_eq!(result, error!(EAGAIN), "Expected EAGAIN in {}", scenario.name);
294 }
295 Event::WriteToSlaveUnexpectedSuccess { data } => {
296 let mut buffer = TestBuffer::new(data.to_bytes());
301 let _ = ld
302 .replica_write(&mut buffer)
303 .expect("replica_write failed (unexpected success case)");
304 }
305 Event::SetPacketMode { enabled } => {
306 ld.set_packet_mode(enabled);
307 }
308 Event::SetTermios { termios: ref termios_config } => {
309 let mut termios = crate::get_default_termios();
310 termios.c_iflag = parse_flags(&termios_config.c_iflag, &iflags);
311 termios.c_oflag = parse_flags(&termios_config.c_oflag, &oflags);
312 termios.c_lflag = parse_flags(&termios_config.c_lflag, &lflags);
313 if let Some(cc) = &termios_config.c_cc {
314 let mapping = get_cc_mapping();
315 for (name, &val) in cc {
316 if let Some(&idx) = mapping.get(name.as_str()) {
317 if idx < termios.c_cc.len() {
318 termios.c_cc[idx] = val;
319 }
320 }
321 }
322 }
323 let _ = ld.set_termios(termios);
324 }
325 Event::Flush { side, queue_selector } => {
326 let is_main = match side.as_str() {
327 "main" => true,
328 "replica" => false,
329 _ => panic!("Unknown side {}", side),
330 };
331 let queue_selector_val = match queue_selector.as_str() {
332 "TCIFLUSH" => starnix_uapi::TCIFLUSH,
333 "TCOFLUSH" => starnix_uapi::TCOFLUSH,
334 "TCIOFLUSH" => starnix_uapi::TCIOFLUSH,
335 _ => panic!("Unknown queue_selector {}", queue_selector),
336 };
337 ld.flush(is_main, queue_selector_val).expect("flush failed");
338 }
339 Event::WaitUntilReadable { side } => {
340 struct ZeroSizedBuffer {}
345 impl OutputBuffer for ZeroSizedBuffer {
346 fn write(&mut self, _data: &[u8]) -> Result<usize, Errno> {
347 Ok(0)
348 }
349 }
350 let mut buf = ZeroSizedBuffer {};
351
352 match side.as_str() {
353 "main" => {
354 assert_eq!(Ok(0), ld.main_read(&mut buf));
355 }
356 "replica" => {
357 assert_eq!(Ok(0), ld.replica_read(&mut buf));
358 }
359 _ => panic!("Unknown side {}", side),
360 };
361 }
362 }
363 }
364}