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}
70
71struct TestBuffer {
72 data: Vec<u8>,
73}
74
75impl TestBuffer {
76 fn new(data: Vec<u8>) -> Self {
77 Self { data }
78 }
79}
80
81impl InputBuffer for TestBuffer {
82 fn available(&self) -> usize {
83 self.data.len()
84 }
85 fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno> {
86 if size > self.data.len() {
87 return error!(EAGAIN);
88 }
89 let result = self.data.drain(0..size).collect();
90 Ok(result)
91 }
92}
93
94struct TestOutputBuffer {
95 data: Vec<u8>,
96}
97
98impl TestOutputBuffer {
99 fn new() -> Self {
100 Self { data: vec![] }
101 }
102}
103
104impl OutputBuffer for TestOutputBuffer {
105 fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
106 self.data.extend_from_slice(data);
107 Ok(data.len())
108 }
109}
110
111fn parse_flags(flags: &[String], mapping: &[(u32, &str)]) -> u32 {
112 let mut result = 0;
113 for flag in flags {
114 if let Some((val, _)) = mapping.iter().find(|(_, name)| name == flag) {
115 result |= val;
116 } else {
117 panic!("Unknown flag {}", flag);
118 }
119 }
120 result
121}
122
123fn get_iflag_mapping() -> Vec<(u32, &'static str)> {
124 vec![
125 (starnix_uapi::IGNBRK, "IGNBRK"),
126 (starnix_uapi::BRKINT, "BRKINT"),
127 (starnix_uapi::IGNPAR, "IGNPAR"),
128 (starnix_uapi::PARMRK, "PARMRK"),
129 (starnix_uapi::INPCK, "INPCK"),
130 (starnix_uapi::ISTRIP, "ISTRIP"),
131 (starnix_uapi::INLCR, "INLCR"),
132 (starnix_uapi::IGNCR, "IGNCR"),
133 (starnix_uapi::ICRNL, "ICRNL"),
134 (starnix_uapi::IUCLC, "IUCLC"),
135 (starnix_uapi::IXON, "IXON"),
136 (starnix_uapi::IXANY, "IXANY"),
137 (starnix_uapi::IXOFF, "IXOFF"),
138 (starnix_uapi::IMAXBEL, "IMAXBEL"),
139 (starnix_uapi::IUTF8, "IUTF8"),
140 ]
141}
142
143fn get_oflag_mapping() -> Vec<(u32, &'static str)> {
144 vec![
145 (starnix_uapi::OPOST, "OPOST"),
146 (starnix_uapi::OLCUC, "OLCUC"),
147 (starnix_uapi::ONLCR, "ONLCR"),
148 (starnix_uapi::OCRNL, "OCRNL"),
149 (starnix_uapi::ONOCR, "ONOCR"),
150 (starnix_uapi::ONLRET, "ONLRET"),
151 (starnix_uapi::OFILL, "OFILL"),
152 (starnix_uapi::OFDEL, "OFDEL"),
153 (starnix_uapi::XTABS, "XTABS"),
154 ]
155}
156
157fn get_lflag_mapping() -> Vec<(u32, &'static str)> {
158 vec![
159 (starnix_uapi::ISIG, "ISIG"),
160 (starnix_uapi::ICANON, "ICANON"),
161 (starnix_uapi::XCASE, "XCASE"),
162 (starnix_uapi::ECHO, "ECHO"),
163 (starnix_uapi::ECHOE, "ECHOE"),
164 (starnix_uapi::ECHOK, "ECHOK"),
165 (starnix_uapi::ECHONL, "ECHONL"),
166 (starnix_uapi::ECHOCTL, "ECHOCTL"),
167 (starnix_uapi::ECHOPRT, "ECHOPRT"),
168 (starnix_uapi::ECHOKE, "ECHOKE"),
169 (starnix_uapi::FLUSHO, "FLUSHO"),
170 (starnix_uapi::NOFLSH, "NOFLSH"),
171 (starnix_uapi::TOSTOP, "TOSTOP"),
172 (starnix_uapi::PENDIN, "PENDIN"),
173 (starnix_uapi::IEXTEN, "IEXTEN"),
174 ]
175}
176
177fn get_cc_mapping() -> HashMap<&'static str, usize> {
178 let mut m = HashMap::new();
179 m.insert("VMIN", starnix_uapi::VMIN as usize);
180 m.insert("VTIME", starnix_uapi::VTIME as usize);
181 m.insert("VINTR", starnix_uapi::VINTR as usize);
182 m.insert("VQUIT", starnix_uapi::VQUIT as usize);
183 m.insert("VERASE", starnix_uapi::VERASE as usize);
184 m.insert("VKILL", starnix_uapi::VKILL as usize);
185 m.insert("VEOF", starnix_uapi::VEOF as usize);
186 m.insert("VSTART", starnix_uapi::VSTART as usize);
187 m.insert("VSTOP", starnix_uapi::VSTOP as usize);
188 m.insert("VSUSP", starnix_uapi::VSUSP as usize);
189 m.insert("VEOL", starnix_uapi::VEOL as usize);
190 m.insert("VREPRINT", starnix_uapi::VREPRINT as usize);
191 m.insert("VDISCARD", starnix_uapi::VDISCARD as usize);
192 m.insert("VWERASE", starnix_uapi::VWERASE as usize);
193 m.insert("VLNEXT", starnix_uapi::VLNEXT as usize);
194 m.insert("VEOL2", starnix_uapi::VEOL2 as usize);
195 m
196}
197
198pub fn test_replay_trace(name: &str, json_data: &str) {
199 println!("Running trace: {}", name);
200 let scenario: Scenario = serde_json::from_str(json_data).unwrap_or_else(|e| {
201 panic!("Failed to parse trace {}: {}", name, e);
202 });
203 run_scenario(scenario);
204}
205
206fn run_scenario(scenario: Scenario) {
207 let iflags = get_iflag_mapping();
208 let oflags = get_oflag_mapping();
209 let lflags = get_lflag_mapping();
210
211 let mut ld = LineDiscipline::default();
212 ld.main_open();
213 ld.replica_open();
214
215 let mut termios = crate::get_default_termios();
217 termios.c_iflag = parse_flags(&scenario.initial_termios.c_iflag, &iflags);
218 termios.c_oflag = parse_flags(&scenario.initial_termios.c_oflag, &oflags);
219 termios.c_lflag = parse_flags(&scenario.initial_termios.c_lflag, &lflags);
220
221 if let Some(cc) = &scenario.initial_termios.c_cc {
222 let mapping = get_cc_mapping();
223 for (name, &val) in cc {
224 if let Some(&idx) = mapping.get(name.as_str()) {
225 if idx < termios.c_cc.len() {
226 termios.c_cc[idx] = val;
227 }
228 } else {
229 panic!("Unknown c_cc name {}", name);
231 }
232 }
233 }
234
235 let _ = ld.set_termios(termios);
236
237 for event in scenario.events {
238 match event {
239 Event::WriteToMaster { data } => {
240 let mut buffer = TestBuffer::new(data.to_bytes());
241 let _ = ld.main_write(&mut buffer).expect("main_write failed");
242 }
243 Event::ReadFromMaster { data } => {
244 let mut buffer = TestOutputBuffer::new();
245 loop {
246 match ld.main_read(&mut buffer) {
247 Ok(_) => {}
248 Err(e) if e == (error!(EAGAIN) as Result<(), Errno>).unwrap_err() => {
249 break;
250 }
251 Err(e) => panic!("main_read failed: {:?}", e),
252 }
253 }
254 assert_eq!(
255 String::from_utf8_lossy(&buffer.data),
256 String::from_utf8_lossy(&data.to_bytes()),
257 "ReadFromMaster mismatch in {}",
258 scenario.name
259 );
260 }
261 Event::ReadFromSlave { data } => {
262 let mut buffer = TestOutputBuffer::new();
263 loop {
264 match ld.replica_read(&mut buffer) {
265 Ok(_) => {}
266 Err(e) if e == (error!(EAGAIN) as Result<(), Errno>).unwrap_err() => {
267 break;
268 }
269 Err(e) => panic!("replica_read failed: {:?}", e),
270 }
271 }
272 assert_eq!(
273 String::from_utf8_lossy(&buffer.data),
274 String::from_utf8_lossy(&data.to_bytes()),
275 "ReadFromSlave mismatch in {}",
276 scenario.name
277 );
278 }
279 Event::WriteToSlave { data } => {
280 let mut buffer = TestBuffer::new(data.to_bytes());
281 let _ = ld.replica_write(&mut buffer).expect("replica_write failed");
282 }
283 Event::WriteToSlaveBlocked { data } => {
284 let mut buffer = TestBuffer::new(data.to_bytes());
285 let result = ld.replica_write(&mut buffer);
286 assert!(
287 result.is_err(),
288 "Expected replica_write to block/fail in {}, but it succeeded",
289 scenario.name
290 );
291 assert_eq!(result, error!(EAGAIN), "Expected EAGAIN in {}", scenario.name);
292 }
293 Event::WriteToSlaveUnexpectedSuccess { data } => {
294 let mut buffer = TestBuffer::new(data.to_bytes());
299 let _ = ld
300 .replica_write(&mut buffer)
301 .expect("replica_write failed (unexpected success case)");
302 }
303 Event::SetPacketMode { enabled } => {
304 ld.set_packet_mode(enabled);
305 }
306 Event::SetTermios { termios: ref termios_config } => {
307 let mut termios = crate::get_default_termios();
308 termios.c_iflag = parse_flags(&termios_config.c_iflag, &iflags);
309 termios.c_oflag = parse_flags(&termios_config.c_oflag, &oflags);
310 termios.c_lflag = parse_flags(&termios_config.c_lflag, &lflags);
311 if let Some(cc) = &termios_config.c_cc {
312 let mapping = get_cc_mapping();
313 for (name, &val) in cc {
314 if let Some(&idx) = mapping.get(name.as_str()) {
315 if idx < termios.c_cc.len() {
316 termios.c_cc[idx] = val;
317 }
318 }
319 }
320 }
321 let _ = ld.set_termios(termios);
322 }
323 Event::Flush { side, queue_selector } => {
324 let is_main = match side.as_str() {
325 "main" => true,
326 "replica" => false,
327 _ => panic!("Unknown side {}", side),
328 };
329 let queue_selector_val = match queue_selector.as_str() {
330 "TCIFLUSH" => starnix_uapi::TCIFLUSH,
331 "TCOFLUSH" => starnix_uapi::TCOFLUSH,
332 "TCIOFLUSH" => starnix_uapi::TCIOFLUSH,
333 _ => panic!("Unknown queue_selector {}", queue_selector),
334 };
335 ld.flush(is_main, queue_selector_val).expect("flush failed");
336 }
337 }
338 }
339}