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 final_termios: TermiosConfig,
16}
17
18#[derive(Deserialize, Debug)]
19struct TermiosConfig {
20 #[serde(default)]
21 c_iflag: Vec<String>,
22 #[serde(default)]
23 c_oflag: Vec<String>,
24 #[serde(default)]
25 c_lflag: Vec<String>,
26 #[allow(dead_code)]
27 c_cflag: Option<u32>, c_cc: Option<HashMap<String, u8>>,
29}
30
31#[derive(Deserialize, Debug)]
32#[serde(untagged)]
33enum TraceData {
34 Bytes(Vec<u8>),
35 String(String),
36}
37
38impl TraceData {
39 fn to_bytes(&self) -> Vec<u8> {
40 match self {
41 TraceData::Bytes(b) => b.clone(),
42 TraceData::String(s) => s.as_bytes().to_vec(),
43 }
44 }
45}
46
47fn default_read_size() -> usize {
48 4096
49}
50
51#[derive(Deserialize, Debug)]
52#[serde(tag = "type")]
53enum Event {
54 #[serde(rename = "write_to_master")]
55 WriteToMaster {
56 data: TraceData,
57 #[serde(default)]
58 signals: Option<Vec<String>>,
59 },
60 #[serde(rename = "write_to_master_blocked")]
61 WriteToMasterBlocked { data: TraceData },
62 #[serde(rename = "read_from_master")]
63 ReadFromMaster { data: TraceData },
64 #[serde(rename = "read_from_slave")]
65 ReadFromSlave {
66 data: TraceData,
67 #[serde(default)]
68 signals: Option<Vec<String>>,
69 },
70 #[serde(rename = "read_once_from_master")]
71 ReadOnceFromMaster {
72 #[serde(default = "default_read_size")]
73 size: usize,
74 data: TraceData,
75 },
76 #[serde(rename = "read_once_from_master_blocked")]
77 ReadOnceFromMasterBlocked {
78 #[serde(default = "default_read_size")]
79 size: usize,
80 },
81 #[serde(rename = "read_once_from_master_eio")]
82 ReadOnceFromMasterEio {
83 #[serde(default = "default_read_size")]
84 size: usize,
85 },
86 #[serde(rename = "read_once_from_slave")]
87 ReadOnceFromSlave {
88 #[serde(default = "default_read_size")]
89 size: usize,
90 data: TraceData,
91 #[serde(default)]
92 signals: Option<Vec<String>>,
93 },
94 #[serde(rename = "read_once_from_slave_blocked")]
95 ReadOnceFromSlaveBlocked {
96 #[serde(default = "default_read_size")]
97 size: usize,
98 },
99 #[serde(rename = "write_to_slave")]
100 WriteToSlave { data: TraceData },
101 #[serde(rename = "write_to_slave_blocked")]
102 WriteToSlaveBlocked { data: TraceData },
103 #[serde(rename = "write_to_slave_eio")]
104 WriteToSlaveEio { data: TraceData },
105 #[serde(rename = "set_packet_mode")]
106 SetPacketMode { enabled: bool },
107 #[serde(rename = "set_termios")]
108 SetTermios {
109 termios: TermiosConfig,
110 #[serde(default)]
111 signals: Option<Vec<String>>,
112 },
113 #[serde(rename = "flush")]
114 Flush { side: String, queue_selector: String },
115 #[serde(rename = "wait_until_readable")]
116 WaitUntilReadable { side: String },
117 #[serde(rename = "check_readable_size")]
118 CheckReadableSize { side: String, size: usize },
119 #[serde(rename = "check_poll")]
120 CheckPoll { side: String, events: Vec<String> },
121 #[serde(rename = "close")]
122 Close { side: String },
123}
124
125struct TestBuffer {
126 data: Vec<u8>,
127}
128
129impl TestBuffer {
130 fn new(data: Vec<u8>) -> Self {
131 Self { data }
132 }
133}
134
135impl InputBuffer for TestBuffer {
136 fn available(&self) -> usize {
137 self.data.len()
138 }
139 fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno> {
140 if size > self.data.len() {
141 return error!(EAGAIN);
142 }
143 let result = self.data.drain(0..size).collect();
144 Ok(result)
145 }
146}
147
148struct TestOutputBuffer {
149 data: Vec<u8>,
150}
151
152impl TestOutputBuffer {
153 fn new() -> Self {
154 Self { data: vec![] }
155 }
156}
157
158impl OutputBuffer for TestOutputBuffer {
159 fn available(&self) -> usize {
160 usize::MAX
161 }
162 fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
163 self.data.extend_from_slice(data);
164 Ok(data.len())
165 }
166}
167
168struct LimitedTestOutputBuffer {
169 data: Vec<u8>,
170 max_size: usize,
171}
172
173impl LimitedTestOutputBuffer {
174 fn new(max_size: usize) -> Self {
175 Self { data: vec![], max_size }
176 }
177}
178
179impl OutputBuffer for LimitedTestOutputBuffer {
180 fn available(&self) -> usize {
181 self.max_size.saturating_sub(self.data.len())
182 }
183 fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
184 let to_write = std::cmp::min(self.available(), data.len());
185 self.data.extend_from_slice(&data[..to_write]);
186 Ok(to_write)
187 }
188}
189
190fn parse_flags(flags: &[String], mapping: &[(u32, &str)]) -> u32 {
191 let mut result = 0;
192 for flag in flags {
193 if let Some((val, _)) = mapping.iter().find(|(_, name)| name == flag) {
194 result |= val;
195 } else {
196 panic!("Unknown flag {}", flag);
197 }
198 }
199 result
200}
201
202fn get_iflag_mapping() -> Vec<(u32, &'static str)> {
203 vec![
204 (starnix_uapi::IGNBRK, "IGNBRK"),
205 (starnix_uapi::BRKINT, "BRKINT"),
206 (starnix_uapi::IGNPAR, "IGNPAR"),
207 (starnix_uapi::PARMRK, "PARMRK"),
208 (starnix_uapi::INPCK, "INPCK"),
209 (starnix_uapi::ISTRIP, "ISTRIP"),
210 (starnix_uapi::INLCR, "INLCR"),
211 (starnix_uapi::IGNCR, "IGNCR"),
212 (starnix_uapi::ICRNL, "ICRNL"),
213 (starnix_uapi::IUCLC, "IUCLC"),
214 (starnix_uapi::IXON, "IXON"),
215 (starnix_uapi::IXANY, "IXANY"),
216 (starnix_uapi::IXOFF, "IXOFF"),
217 (starnix_uapi::IMAXBEL, "IMAXBEL"),
218 (starnix_uapi::IUTF8, "IUTF8"),
219 ]
220}
221
222fn get_oflag_mapping() -> Vec<(u32, &'static str)> {
223 vec![
224 (starnix_uapi::OPOST, "OPOST"),
225 (starnix_uapi::OLCUC, "OLCUC"),
226 (starnix_uapi::ONLCR, "ONLCR"),
227 (starnix_uapi::OCRNL, "OCRNL"),
228 (starnix_uapi::ONOCR, "ONOCR"),
229 (starnix_uapi::ONLRET, "ONLRET"),
230 (starnix_uapi::OFILL, "OFILL"),
231 (starnix_uapi::OFDEL, "OFDEL"),
232 (starnix_uapi::XTABS, "XTABS"),
233 ]
234}
235
236fn get_lflag_mapping() -> Vec<(u32, &'static str)> {
237 vec![
238 (starnix_uapi::ISIG, "ISIG"),
239 (starnix_uapi::ICANON, "ICANON"),
240 (starnix_uapi::XCASE, "XCASE"),
241 (starnix_uapi::ECHO, "ECHO"),
242 (starnix_uapi::ECHOE, "ECHOE"),
243 (starnix_uapi::ECHOK, "ECHOK"),
244 (starnix_uapi::ECHONL, "ECHONL"),
245 (starnix_uapi::ECHOCTL, "ECHOCTL"),
246 (starnix_uapi::ECHOPRT, "ECHOPRT"),
247 (starnix_uapi::ECHOKE, "ECHOKE"),
248 (starnix_uapi::FLUSHO, "FLUSHO"),
249 (starnix_uapi::NOFLSH, "NOFLSH"),
250 (starnix_uapi::TOSTOP, "TOSTOP"),
251 (starnix_uapi::PENDIN, "PENDIN"),
252 (starnix_uapi::IEXTEN, "IEXTEN"),
253 (starnix_uapi::EXTPROC, "EXTPROC"),
254 ]
255}
256
257fn get_cc_mapping() -> HashMap<&'static str, usize> {
258 let mut m = HashMap::new();
259 m.insert("VMIN", starnix_uapi::VMIN as usize);
260 m.insert("VTIME", starnix_uapi::VTIME as usize);
261 m.insert("VINTR", starnix_uapi::VINTR as usize);
262 m.insert("VQUIT", starnix_uapi::VQUIT as usize);
263 m.insert("VERASE", starnix_uapi::VERASE as usize);
264 m.insert("VKILL", starnix_uapi::VKILL as usize);
265 m.insert("VEOF", starnix_uapi::VEOF as usize);
266 m.insert("VSTART", starnix_uapi::VSTART as usize);
267 m.insert("VSTOP", starnix_uapi::VSTOP as usize);
268 m.insert("VSUSP", starnix_uapi::VSUSP as usize);
269 m.insert("VEOL", starnix_uapi::VEOL as usize);
270 m.insert("VREPRINT", starnix_uapi::VREPRINT as usize);
271 m.insert("VDISCARD", starnix_uapi::VDISCARD as usize);
272 m.insert("VWERASE", starnix_uapi::VWERASE as usize);
273 m.insert("VLNEXT", starnix_uapi::VLNEXT as usize);
274 m.insert("VEOL2", starnix_uapi::VEOL2 as usize);
275 m
276}
277
278fn signal_to_name(sig: Signal) -> &'static str {
279 if sig == SIGINT {
280 "SIGINT"
281 } else if sig == SIGQUIT {
282 "SIGQUIT"
283 } else if sig == SIGTSTP {
284 "SIGTSTP"
285 } else {
286 panic!("Unexpected signal {:?}", sig);
287 }
288}
289
290fn check_signals(
291 pending_signals: PendingSignals,
292 expected_signals: Option<Vec<String>>,
293 event_name: &str,
294 scenario_name: &str,
295) {
296 let actual: Vec<String> =
297 pending_signals.signals().iter().map(|&s| signal_to_name(s).to_string()).collect();
298 let expected = expected_signals.unwrap_or_default();
299 assert_eq!(actual, expected, "{} signals mismatch in {}", event_name, scenario_name);
300}
301
302fn decompose_fd_events(events: FdEvents) -> Vec<String> {
303 let mut out = Vec::new();
304 if events.contains(FdEvents::POLLIN) {
305 out.push("POLLIN".to_string());
306 }
307 if events.contains(FdEvents::POLLOUT) {
308 out.push("POLLOUT".to_string());
309 }
310 if events.contains(FdEvents::POLLPRI) {
311 out.push("POLLPRI".to_string());
312 }
313 if events.contains(FdEvents::POLLERR) {
314 out.push("POLLERR".to_string());
315 }
316 if events.contains(FdEvents::POLLHUP) {
317 out.push("POLLHUP".to_string());
318 }
319 out
320}
321
322pub fn test_replay_trace(name: &str, json_data: &str) {
323 println!("Running trace: {}", name);
324 let scenario: Scenario = serde_json::from_str(json_data).unwrap_or_else(|e| {
325 panic!("Failed to parse trace {}: {}", name, e);
326 });
327 run_scenario(scenario);
328}
329
330fn apply_termios_config(
331 mut termios: uapi::termios2,
332 config: &TermiosConfig,
333 iflags: &[(u32, &'static str)],
334 oflags: &[(u32, &'static str)],
335 lflags: &[(u32, &'static str)],
336) -> uapi::termios2 {
337 termios.c_iflag = parse_flags(&config.c_iflag, iflags);
338 termios.c_oflag = parse_flags(&config.c_oflag, oflags);
339 termios.c_lflag = parse_flags(&config.c_lflag, lflags);
340 if let Some(cc) = &config.c_cc {
341 let mapping = get_cc_mapping();
342 for (name, &val) in cc {
343 if let Some(&idx) = mapping.get(name.as_str()) {
344 if idx < termios.c_cc.len() {
345 termios.c_cc[idx] = val;
346 }
347 } else {
348 panic!("Unknown c_cc name {}", name);
349 }
350 }
351 }
352 termios
353}
354
355fn run_scenario(scenario: Scenario) {
356 let iflags = get_iflag_mapping();
357 let oflags = get_oflag_mapping();
358 let lflags = get_lflag_mapping();
359
360 let mut ld = LineDiscipline::default();
361 ld.main_open();
362 ld.replica_open();
363
364 let termios =
366 apply_termios_config(*ld.termios(), &scenario.initial_termios, &iflags, &oflags, &lflags);
367 let initial_signals = ld.set_termios(termios);
368 assert!(initial_signals.signals().is_empty());
369
370 for event in scenario.events {
371 match event {
372 Event::WriteToMaster { data, signals: expected_signals } => {
373 let bytes = data.to_bytes();
374 let mut buffer = TestBuffer::new(bytes.clone());
375 let (written, pending_signals) =
376 ld.main_write(&mut buffer).expect("main_write failed");
377 assert_eq!(
378 written,
379 bytes.len(),
380 "WriteToMaster partial write in {}",
381 scenario.name
382 );
383 check_signals(pending_signals, expected_signals, "WriteToMaster", &scenario.name);
384 }
385 Event::WriteToMasterBlocked { data } => {
386 let mut buffer = TestBuffer::new(data.to_bytes());
387 assert_eq!(
388 ld.main_write(&mut buffer),
389 error!(EAGAIN),
390 "Expected main_write to return EAGAIN in {}",
391 scenario.name
392 );
393 }
394 Event::ReadFromMaster { data } => {
395 let mut buffer = TestOutputBuffer::new();
396 loop {
397 match ld.main_read(&mut buffer) {
398 Ok(_) => {}
399 Err(e) if e == (error!(EAGAIN) as Result<(), Errno>).unwrap_err() => {
400 break;
401 }
402 Err(e) => panic!("main_read failed: {:?}", e),
403 }
404 }
405 assert_eq!(
406 buffer.data,
407 data.to_bytes(),
408 "ReadFromMaster mismatch in {} (actual={:?}, expected={:?})",
409 scenario.name,
410 String::from_utf8_lossy(&buffer.data),
411 String::from_utf8_lossy(&data.to_bytes()),
412 );
413 }
414 Event::ReadFromSlave { data, signals: expected_signals } => {
415 let mut buffer = TestOutputBuffer::new();
416 let mut pending_signals = PendingSignals::new();
417 loop {
418 match ld.replica_read(&mut buffer) {
419 Ok((_, signals)) => pending_signals.append(signals),
420 Err(e) if e == (error!(EAGAIN) as Result<(), Errno>).unwrap_err() => {
421 break;
422 }
423 Err(e) => panic!("replica_read failed: {:?}", e),
424 }
425 }
426 check_signals(pending_signals, expected_signals, "ReadFromSlave", &scenario.name);
427 assert_eq!(
428 buffer.data,
429 data.to_bytes(),
430 "ReadFromSlave mismatch in {} (actual={:?}, expected={:?})",
431 scenario.name,
432 String::from_utf8_lossy(&buffer.data),
433 String::from_utf8_lossy(&data.to_bytes()),
434 );
435 }
436 Event::ReadOnceFromMaster { size, data } => {
437 let mut buffer = LimitedTestOutputBuffer::new(size);
438 let expected = data.to_bytes();
439 let n = ld.main_read(&mut buffer).unwrap_or_else(|e| {
440 panic!("ReadOnceFromMaster failed in {}: {:?}", scenario.name, e)
441 });
442 assert_eq!(
443 n,
444 expected.len(),
445 "ReadOnceFromMaster length mismatch in {}",
446 scenario.name
447 );
448 assert_eq!(
449 buffer.data,
450 expected,
451 "ReadOnceFromMaster data mismatch in {} (actual={:?}, expected={:?})",
452 scenario.name,
453 String::from_utf8_lossy(&buffer.data),
454 String::from_utf8_lossy(&expected),
455 );
456 }
457 Event::ReadOnceFromMasterBlocked { size } => {
458 let mut buffer = LimitedTestOutputBuffer::new(size);
459 assert_eq!(
460 ld.main_read(&mut buffer),
461 error!(EAGAIN),
462 "Expected ReadOnceFromMaster to return EAGAIN in {}",
463 scenario.name
464 );
465 }
466 Event::ReadOnceFromMasterEio { size } => {
467 let mut buffer = LimitedTestOutputBuffer::new(size);
468 assert_eq!(
469 ld.main_read(&mut buffer),
470 error!(EIO),
471 "Expected ReadOnceFromMaster to return EIO in {}",
472 scenario.name
473 );
474 }
475 Event::ReadOnceFromSlave { size, data, signals: expected_signals } => {
476 let mut buffer = LimitedTestOutputBuffer::new(size);
477 let expected = data.to_bytes();
478 let (n, pending_signals) = ld.replica_read(&mut buffer).unwrap_or_else(|e| {
479 panic!("ReadOnceFromSlave failed in {}: {:?}", scenario.name, e)
480 });
481 check_signals(
482 pending_signals,
483 expected_signals,
484 "ReadOnceFromSlave",
485 &scenario.name,
486 );
487 assert_eq!(
488 n,
489 expected.len(),
490 "ReadOnceFromSlave length mismatch in {}",
491 scenario.name
492 );
493 assert_eq!(
494 buffer.data,
495 expected,
496 "ReadOnceFromSlave data mismatch in {} (actual={:?}, expected={:?})",
497 scenario.name,
498 String::from_utf8_lossy(&buffer.data),
499 String::from_utf8_lossy(&expected),
500 );
501 }
502 Event::ReadOnceFromSlaveBlocked { size } => {
503 let mut buffer = LimitedTestOutputBuffer::new(size);
504 assert_eq!(
505 ld.replica_read(&mut buffer),
506 error!(EAGAIN),
507 "Expected ReadOnceFromSlave to return EAGAIN in {}",
508 scenario.name
509 );
510 }
511 Event::WriteToSlave { data } => {
512 let bytes = data.to_bytes();
513 let mut buffer = TestBuffer::new(bytes.clone());
514 let written = ld.replica_write(&mut buffer).expect("replica_write failed");
515 assert_eq!(written, bytes.len(), "WriteToSlave partial write in {}", scenario.name);
516 }
517 Event::WriteToSlaveBlocked { data } => {
518 let mut buffer = TestBuffer::new(data.to_bytes());
519 let result = ld.replica_write(&mut buffer);
520 assert!(
521 result.is_err(),
522 "Expected replica_write to block/fail in {}, but it succeeded",
523 scenario.name
524 );
525 assert_eq!(result, error!(EAGAIN), "Expected EAGAIN in {}", scenario.name);
526 }
527 Event::WriteToSlaveEio { data } => {
528 let mut buffer = TestBuffer::new(data.to_bytes());
529 assert_eq!(
530 ld.replica_write(&mut buffer),
531 error!(EIO),
532 "Expected replica_write to return EIO in {}",
533 scenario.name
534 );
535 }
536 Event::SetPacketMode { enabled } => {
537 ld.set_packet_mode(enabled);
538 }
539 Event::SetTermios { termios: ref termios_config, signals: expected_signals } => {
540 let termios =
541 apply_termios_config(*ld.termios(), termios_config, &iflags, &oflags, &lflags);
542 let pending_signals = ld.set_termios(termios);
543 check_signals(pending_signals, expected_signals, "SetTermios", &scenario.name);
544 }
545 Event::Flush { side, queue_selector } => {
546 let side = match side.as_str() {
547 "main" => TerminalSide::Main,
548 "replica" => TerminalSide::Replica,
549 _ => panic!("Unknown side {}", side),
550 };
551 let queue_selector_val = match queue_selector.as_str() {
552 "TCIFLUSH" => starnix_uapi::TCIFLUSH,
553 "TCOFLUSH" => starnix_uapi::TCOFLUSH,
554 "TCIOFLUSH" => starnix_uapi::TCIOFLUSH,
555 _ => panic!("Unknown queue_selector {}", queue_selector),
556 };
557 ld.flush(side, queue_selector_val).expect("flush failed");
558 }
559 Event::WaitUntilReadable { side } => {
560 let events = match side.as_str() {
561 "main" => ld.main_query_events(),
562 "replica" => ld.replica_query_events(),
563 _ => panic!("Unknown side {}", side),
564 };
565 assert!(
566 events.intersects(FdEvents::POLLIN | FdEvents::POLLHUP),
567 "Expected {} to be readable in {}, got {:?}",
568 side,
569 scenario.name,
570 events
571 );
572 }
573 Event::CheckReadableSize { side, size } => {
574 let terminal_side = match side.as_str() {
575 "main" => TerminalSide::Main,
576 "replica" => TerminalSide::Replica,
577 _ => panic!("Unknown side {}", side),
578 };
579 assert_eq!(
580 ld.get_available_read_size(terminal_side),
581 size,
582 "CheckReadableSize ({}) mismatch in {}",
583 side,
584 scenario.name
585 );
586 }
587 Event::CheckPoll { side, events } => {
588 let actual_events = match side.as_str() {
589 "main" => ld.main_query_events(),
590 "replica" => ld.replica_query_events(),
591 _ => panic!("Unknown side {}", side),
592 };
593 assert_eq!(
594 decompose_fd_events(actual_events),
595 events,
596 "CheckPoll ({}) mismatch in {}",
597 side,
598 scenario.name
599 );
600 }
601 Event::Close { side } => match side.as_str() {
602 "main" => ld.main_close(),
603 "replica" => ld.replica_close(),
604 _ => panic!("Unknown side {}", side),
605 },
606 }
607 }
608
609 assert_eq!(
610 ld.termios().c_iflag,
611 parse_flags(&scenario.final_termios.c_iflag, &iflags),
612 "final_termios.c_iflag mismatch in {}",
613 scenario.name
614 );
615 assert_eq!(
616 ld.termios().c_oflag,
617 parse_flags(&scenario.final_termios.c_oflag, &oflags),
618 "final_termios.c_oflag mismatch in {}",
619 scenario.name
620 );
621 assert_eq!(
622 ld.termios().c_lflag,
623 parse_flags(&scenario.final_termios.c_lflag, &lflags),
624 "final_termios.c_lflag mismatch in {}",
625 scenario.name
626 );
627}