diff --git a/crates/nucleus-test-sdk/src/serial.rs b/crates/nucleus-test-sdk/src/serial.rs index f247a05..81f451d 100644 --- a/crates/nucleus-test-sdk/src/serial.rs +++ b/crates/nucleus-test-sdk/src/serial.rs @@ -62,7 +62,12 @@ impl Serial { match p.read(&mut buf) { Ok(0) => Ok(None), Ok(_) => Ok(Some(buf[0])), - Err(e) if e.kind() == std::io::ErrorKind::TimedOut => Ok(None), + Err(e) + if e.kind() == std::io::ErrorKind::WouldBlock + || e.kind() == std::io::ErrorKind::TimedOut => + { + Ok(None) + } Err(e) => Err(e), } } @@ -105,4 +110,118 @@ mod tests { let mut s = Serial::open_tcp(&addr.to_string()).unwrap(); assert_eq!(s.read_byte(Duration::from_millis(50)).unwrap(), None); } + + /// A `serialport::SerialPort` stub whose `read` always returns + /// `ErrorKind::WouldBlock` — some serialport backends report a timed-out + /// read this way instead of `ErrorKind::TimedOut`. + struct WouldBlockPort; + + impl std::io::Read for WouldBlockPort { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Err(std::io::Error::from(std::io::ErrorKind::WouldBlock)) + } + } + + impl std::io::Write for WouldBlockPort { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + Ok(buf.len()) + } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + } + + impl serialport::SerialPort for WouldBlockPort { + fn name(&self) -> Option { + None + } + fn baud_rate(&self) -> serialport::Result { + Ok(115200) + } + fn data_bits(&self) -> serialport::Result { + Ok(serialport::DataBits::Eight) + } + fn flow_control(&self) -> serialport::Result { + Ok(serialport::FlowControl::None) + } + fn parity(&self) -> serialport::Result { + Ok(serialport::Parity::None) + } + fn stop_bits(&self) -> serialport::Result { + Ok(serialport::StopBits::One) + } + fn timeout(&self) -> Duration { + Duration::from_millis(50) + } + fn set_baud_rate(&mut self, _baud_rate: u32) -> serialport::Result<()> { + Ok(()) + } + fn set_data_bits(&mut self, _data_bits: serialport::DataBits) -> serialport::Result<()> { + Ok(()) + } + fn set_flow_control( + &mut self, + _flow_control: serialport::FlowControl, + ) -> serialport::Result<()> { + Ok(()) + } + fn set_parity(&mut self, _parity: serialport::Parity) -> serialport::Result<()> { + Ok(()) + } + fn set_stop_bits(&mut self, _stop_bits: serialport::StopBits) -> serialport::Result<()> { + Ok(()) + } + fn set_timeout(&mut self, _timeout: Duration) -> serialport::Result<()> { + Ok(()) + } + fn write_request_to_send(&mut self, _level: bool) -> serialport::Result<()> { + Ok(()) + } + fn write_data_terminal_ready(&mut self, _level: bool) -> serialport::Result<()> { + Ok(()) + } + fn read_clear_to_send(&mut self) -> serialport::Result { + Ok(false) + } + fn read_data_set_ready(&mut self) -> serialport::Result { + Ok(false) + } + fn read_ring_indicator(&mut self) -> serialport::Result { + Ok(false) + } + fn read_carrier_detect(&mut self) -> serialport::Result { + Ok(false) + } + fn bytes_to_read(&self) -> serialport::Result { + Ok(0) + } + fn bytes_to_write(&self) -> serialport::Result { + Ok(0) + } + fn clear(&self, _buffer_to_clear: serialport::ClearBuffer) -> serialport::Result<()> { + Ok(()) + } + fn try_clone(&self) -> serialport::Result> { + Err(serialport::Error::new( + serialport::ErrorKind::Unknown, + "clone not supported in test stub", + )) + } + fn set_break(&self) -> serialport::Result<()> { + Ok(()) + } + fn clear_break(&self) -> serialport::Result<()> { + Ok(()) + } + } + + #[test] + fn port_read_byte_treats_would_block_as_a_timeout_not_an_error() { + // Issue #51: the `Port` branch only caught `ErrorKind::TimedOut`, + // unlike the `Tcp` branch's `TimedOut | WouldBlock`. Some + // serialport backends report a timed-out read as `WouldBlock`, + // which used to surface as `Err` here instead of `Ok(None)`. + let mut s = Serial::Port(Box::new(WouldBlockPort)); + assert_eq!(s.read_byte(Duration::from_millis(10)).unwrap(), None); + } }