use turkos_os_syscall_abi::syscall_abi::Errno; use turkos_os_user_runtime_io::io::{self, Error, Fault, Outcome, Reader, Writer}; struct Serial { replies: Vec>, attempts: Vec>, output: Vec, retained_handles: usize, } impl Serial { fn new(replies: Vec>) -> Self { Self { replies, attempts: vec![], output: vec![], retained_handles: 1 } } } impl Writer for Serial { fn write_once(&mut self, bytes: &[u8]) -> Result { self.attempts.push(bytes.to_vec()); let reply = self.replies.remove(0); if let Ok(count) = reply { if count <= bytes.len() { self.output.extend_from_slice(&bytes[..count]); } } reply } } #[test] fn os_user_api_io_positive() { let payload = b"serial:abcde"; let mut serial = Serial::new(vec![Ok(2), Ok(4), Ok(6)]); let outcome = io::write_all(&mut serial, payload, 3).unwrap(); assert_eq!(outcome, Outcome { bytes: 12, calls: 3, interruptions: 0 }); assert_eq!(serial.output, payload); assert_eq!(serial.attempts, vec![ payload.to_vec(), payload[2..].to_vec(), payload[6..].to_vec(), ]); assert_eq!(serial.retained_handles, 1); } #[test] fn os_user_api_io_negative() { let mut serial = Serial::new(vec![Ok(0), Ok(0), Ok(1)]); assert_eq!(io::write_all(&mut serial, b"A", 3), Err(Error::Stalled(Outcome { bytes: 0, calls: 2, interruptions: 0 }))); assert_eq!(serial.attempts.len(), 2); assert!(serial.output.is_empty()); assert_eq!(serial.retained_handles, 1); } #[test] fn interrupts_and_budget_have_separate_results() { let mut serial = Serial::new(vec![Err(Fault::Interrupted), Ok(2), Ok(2)]); assert_eq!(io::write_all(&mut serial, b"ABCD", 2), Err(Error::BudgetExceeded(Outcome { bytes: 2, calls: 2, interruptions: 1 }))); assert_eq!(serial.output, b"AB"); assert_eq!(serial.attempts, vec![b"ABCD".to_vec(), b"ABCD".to_vec()]); assert_eq!(serial.retained_handles, 1); } #[test] fn kernel_error_and_invalid_count_report_exact_partial_progress() { let mut serial = Serial::new(vec![Ok(2), Err(Fault::Kernel(Errno::Io))]); assert_eq!(io::write_all(&mut serial, b"ABCD", 3), Err(Error::Kernel(Errno::Io, Outcome { bytes: 2, calls: 2, interruptions: 0 }))); assert_eq!(serial.output, b"AB"); assert_eq!(serial.retained_handles, 1); let mut invalid = Serial::new(vec![Ok(9)]); assert_eq!(io::write_all(&mut invalid, b"ABCD", 1), Err(Error::InvalidCount(Outcome { bytes: 0, calls: 1, interruptions: 0 }))); assert!(invalid.output.is_empty()); } struct Input { parts: Vec, Fault>> } impl Reader for Input { fn read_once(&mut self, bytes: &mut [u8]) -> Result { let part = self.parts.remove(0)?; let count = part.len().min(bytes.len()); bytes[..count].copy_from_slice(&part[..count]); Ok(count) } } #[test] fn short_reads_and_eof_are_distinct() { let mut input = Input { parts: vec![Ok(b"ab".to_vec()), Err(Fault::Interrupted), Ok(b"cd".to_vec())] }; let mut buffer = [0; 4]; assert_eq!(io::read_exact(&mut input, &mut buffer, 3), Ok(Outcome { bytes: 4, calls: 3, interruptions: 1 })); assert_eq!(&buffer, b"abcd"); let mut eof = Input { parts: vec![Ok(b"a".to_vec()), Ok(vec![])] }; let mut buffer = [0; 2]; assert_eq!(io::read_exact(&mut eof, &mut buffer, 3), Err(Error::EndOfInput(Outcome { bytes: 1, calls: 2, interruptions: 0 }))); assert_eq!(buffer, [b'a', 0]); } #[test] fn malformed_wire_stops_with_borrowed_owner_intact() { let mut serial = Serial::new(vec![Err(Fault::Wire)]); assert_eq!(io::write_all(&mut serial, b"AB", 2), Err(Error::Wire(Outcome { bytes: 0, calls: 1, interruptions: 0 }))); assert!(serial.output.is_empty()); assert_eq!(serial.retained_handles, 1); }