use proptest::prelude::*; use pipeview_core::frame::Framer; use pipeview_core::frame::cobs::{CobsFramer, cobs_decode, cobs_encode}; use pipeview_core::frame::length::{LengthConfig, LengthPrefixedFramer}; use pipeview_core::protocol::Endian; use pipeview_core::protocol::ProtocolDecoder; use pipeview_core::protocol::hex::{HexConfig, HexDecoder}; use pipeview_core::protocol::plot::{PlotConfig, PlotDecoder, SampleType}; use pipeview_core::protocol::text::{TextDecoder, TextEncoding}; proptest! { #![proptest_config(ProptestConfig::with_cases(256))] // ── COBS ───────────────────────────────────────────────────────── #[test] fn cobs_roundtrip_any_payload(payload in prop::collection::vec(any::(), 0..512)) { let encoded = cobs_encode(&payload); let decoded = cobs_decode(&encoded).unwrap(); prop_assert_eq!(&decoded, &payload); let mut framer = CobsFramer::new(1024 * 1024); let mut wire = encoded; wire.push(0x00); let frames = framer.feed(&wire); prop_assert_eq!(frames.len(), 1); prop_assert_eq!(&frames[0], &payload); } // ── Length-prefixed ────────────────────────────────────────────── #[test] fn length_roundtrip_any_payload( len_bytes in prop_oneof![Just(1usize), Just(2usize), Just(4usize)], little_endian in any::(), includes_self in any::(), payload in prop::collection::vec(any::(), 0..200) ) { let endian = if little_endian { Endian::Little } else { Endian::Big }; let raw_len = if includes_self { payload.len() + len_bytes } else { payload.len() }; let mut wire = Vec::with_capacity(len_bytes + payload.len()); match (len_bytes, endian) { (1, _) => wire.push(raw_len as u8), (2, Endian::Big) => wire.extend_from_slice(&(raw_len as u16).to_be_bytes()), (2, Endian::Little) => wire.extend_from_slice(&(raw_len as u16).to_le_bytes()), (4, Endian::Big) => wire.extend_from_slice(&(raw_len as u32).to_be_bytes()), (4, Endian::Little) => wire.extend_from_slice(&(raw_len as u32).to_le_bytes()), _ => unreachable!(), } wire.extend_from_slice(&payload); let config = LengthConfig { len_bytes, endian, length_includes_self: includes_self, max_payload: 1024 * 1024, }; let mut framer = LengthPrefixedFramer::new(config); let frames = framer.feed(&wire); prop_assert_eq!(frames.len(), 1); prop_assert_eq!(&frames[0], &payload); } // ── Protocol decoders never panic on arbitrary bytes ───────────── #[test] fn plot_decode_arbitrary_bytes_never_panics(bytes in prop::collection::vec(any::(), 0..256)) { let decoder = PlotDecoder::new(PlotConfig { sample_type: SampleType::F32, endian: Endian::Little, channels: 2, format: pipeview_core::protocol::plot::PlotFormat::Interleaved, }); let _ = decoder.decode(&bytes); } #[test] fn hex_decode_arbitrary_bytes_never_panics(bytes in prop::collection::vec(any::(), 0..256)) { let decoder = HexDecoder::new(HexConfig::default()); let decoded = decoder.decode(&bytes); prop_assert!(decoded.is_some()); } #[test] fn latin1_decode_arbitrary_bytes_never_panics(bytes in prop::collection::vec(any::(), 0..256)) { let decoder = TextDecoder::new(TextEncoding::Latin1); let decoded = decoder.decode(&bytes); prop_assert!(decoded.is_some()); } }