use { pigeon::{Pack, Reader, Writer, U2, U3, U4, U5, U6, U7}, proptest::{prelude::*, test_runner::Config}, }; macro_rules! readwrite_single { ($name:ident, $t:ty) => { proptest! { #![proptest_config(Config::with_cases(300))] #[test] fn $name ( value in any::<$t>() ) { let mut buf = [0; 128]; let mut writer = Writer::new(&mut buf[..]); writer.write(value).unwrap(); let writer_pos = writer.finish().unwrap(); let mut reader = Reader::new(&buf[..]); let value_read: $t = reader.read().unwrap(); assert_eq!(value, value_read); let reader_pos = reader.position(); assert_eq!(writer_pos, reader_pos); assert_eq!(writer_pos, Pack::size(&value)); } } }; } macro_rules! readwrite_all { ($($name:ident => $t:ty,)*) => { $( readwrite_single!($name, $t); )* }; } readwrite_all!( readwrite_u8 => u8, readwrite_u16 => u16, readwrite_u32 => u32, readwrite_u64 => u64, readwrite_i8 => i8, readwrite_i16 => i16, readwrite_i32 => i32, readwrite_i64 => i64, readwrite_f32 => f32, readwrite_f64 => f64, readwrite_u8_u16_u32_u64 => (u8, u16, u32, u64), readwrite_u64_u32_u16_u8 => (u64, u32, u16, u8), readwrite_i8_i16_i32_i64 => (i8, i16, i32, i64), readwrite_i64_i32_i16_i8 => (i64, i32, i16, i8), ); #[test] fn read_write_unit() { let mut buf = [0; 0]; let mut writer = Writer::new(&mut buf[..]); writer.write(()).unwrap(); writer.finish().unwrap(); let mut reader = Reader::new(&buf[..]); assert_eq!(reader.read::<()>().unwrap(), ()); } #[derive(Debug, Clone, Copy, PartialEq)] enum Item { Bool(bool), U8(u8), U16(u16), U32(u32), U64(u64), I8(i8), I16(i16), I32(i32), I64(i64), F32(f32), F64(f64), U2(U2), U3(U3), U4(U4), U5(U5), U6(U6), U7(U7), Align, } impl Item { fn any() -> impl Strategy { prop_oneof![ prop::bool::ANY.prop_map(Item::Bool), prop::num::u8::ANY.prop_map(Item::U8), prop::num::u16::ANY.prop_map(Item::U16), prop::num::u32::ANY.prop_map(Item::U32), prop::num::u64::ANY.prop_map(Item::U64), prop::num::i8::ANY.prop_map(Item::I8), prop::num::i16::ANY.prop_map(Item::I16), prop::num::i32::ANY.prop_map(Item::I32), prop::num::i64::ANY.prop_map(Item::I64), (-1e-10f32..1e10f32).prop_map(Item::F32), // TODO: surely can be done better (-1e-20f64..1e20f64).prop_map(Item::F64), // TODO: surely can be done better prop::num::u8::ANY .prop_map(|x| U2(x % 2)) .prop_map(Item::U2), prop::num::u8::ANY .prop_map(|x| U3(x % 3)) .prop_map(Item::U3), prop::num::u8::ANY .prop_map(|x| U4(x % 4)) .prop_map(Item::U4), prop::num::u8::ANY .prop_map(|x| U5(x % 5)) .prop_map(Item::U5), prop::num::u8::ANY .prop_map(|x| U6(x % 6)) .prop_map(Item::U6), prop::num::u8::ANY .prop_map(|x| U7(x % 7)) .prop_map(Item::U7), Just(Item::Align), ] } fn write_to_stream(self, writer: &mut Writer) { macro_rules! itemcases { ($($id:ident),*) => { match self { $(Item::$id(inner) => writer.write(inner).unwrap(),)* Item::Align => writer.pad_align(), } } } itemcases!(Bool, U8, U16, U32, U64, I8, I16, I32, I64, F32, F64, U2, U3, U4, U5, U6, U7); } fn expect_in_stream<'a>(self, reader: &mut Reader<'a>) { macro_rules! itemcases { ($($id:ident),*) => { match self { $(Item::$id(inner) => { let other = reader.read().unwrap(); let _type_hint = Item::$id(other); eprintln!("READ GET: {:?}", other); assert_eq!(other, inner); }),* Item::Align => reader.skip_align(), } } } itemcases!(Bool, U8, U16, U32, U64, I8, I16, I32, I64, F32, F64, U2, U3, U4, U5, U6, U7); } fn bit_count(self) -> Option { match self { Item::Bool(_) => Some(1), Item::U8(_) => Some(8), Item::U16(_) => Some(16), Item::U32(_) => Some(32), Item::U64(_) => Some(64), Item::I8(_) => Some(8), Item::I16(_) => Some(16), Item::I32(_) => Some(32), Item::I64(_) => Some(64), Item::F32(_) => Some(32), Item::F64(_) => Some(64), Item::U2(_) => Some(2), Item::U3(_) => Some(3), Item::U4(_) => Some(4), Item::U5(_) => Some(5), Item::U6(_) => Some(6), Item::U7(_) => Some(7), Item::Align => None, } } } proptest! { #![proptest_config(Config::with_cases(500))] #[test] fn readwrite_items( items in prop::collection::vec(Item::any(), 0..32), ) { let mut buf = [0; 256]; let mut writer = Writer::new(&mut buf[..]); let mut bit_count = 0; for item in &items { eprintln!("WRITE: {:?}", item); item.write_to_stream(&mut writer); if let Some(item_bit_count) = item.bit_count() { bit_count += item_bit_count; } else { bit_count = ((bit_count + 7) / 8) * 8; } assert_eq!(writer.position(), (bit_count + 7) / 8); } let len = writer.finish().unwrap(); eprintln!("BUFFER: {:?}", &buf[..len]); let mut reader = Reader::new(&buf[..len]); bit_count = 0; for item in &items { eprintln!("READ EXPECT: {:?}", item); item.expect_in_stream(&mut reader); if let Some(item_bit_count) = item.bit_count() { bit_count += item_bit_count; } else { bit_count = ((bit_count + 7) / 8) * 8; } assert_eq!(reader.position(), (bit_count + 7) / 8); } } }