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