use std::convert::{TryFrom, TryInto}; use std::io::Cursor; use deku::prelude::*; use hexlit::hex; use rstest::*; /// General smoke tests for enums /// TODO: These should be divided into smaller tests #[cfg(feature = "bits")] #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8")] enum TestEnum { #[deku(id = "1")] VarA(u8), #[deku(id = "2")] VarB(#[deku(bits = 4)] u8, #[deku(bits = 4)] u8), #[deku(id = "3")] VarC { #[deku(update = "field_b.len()")] field_a: u8, #[deku(count = "field_a")] field_b: Vec, }, #[deku(id = "4")] VarD( #[deku(update = "field_1.len()")] u8, #[deku(count = "field_0")] Vec, ), #[deku(id_pat = "_")] VarDefault { id: u8, value: u8 }, } #[cfg(feature = "bits")] #[rstest(input,expected, case(&hex!("01AB"), TestEnum::VarA(0xAB)), case(&hex!("0269"), TestEnum::VarB(0b0110, 0b1001)), case(&hex!("0302AABB"), TestEnum::VarC{field_a: 0x02, field_b: vec![0xAA, 0xBB]}), case(&hex!("0402AABB"), TestEnum::VarD(0x02, vec![0xAA, 0xBB])), case(&hex!("FF01"), TestEnum::VarDefault{id: 0xFF, value: 0x01}), #[should_panic(expected = "Parse(\"Too much data\")")] case(&hex!("FFFFFF"), TestEnum::VarA(0xFF)), )] fn test_enum(input: &[u8], expected: TestEnum) { let input = input.to_vec(); let ret_read = TestEnum::try_from(input.as_slice()).unwrap(); assert_eq!(expected, ret_read); let ret_write: Vec = ret_read.try_into().unwrap(); assert_eq!(input, ret_write); } #[test] #[should_panic(expected = "Parse(\"Could not match enum variant id = 2 on enum `TestEnum`\")")] fn test_enum_error() { #[derive(DekuRead)] #[deku(id_type = "u8")] enum TestEnum { #[deku(id = "1")] VarA(u8), } let test_data = &mut [0x02, 0x02]; let _ret_read = TestEnum::try_from(test_data.as_slice()).unwrap(); } #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8")] enum TestEnumDiscriminant { VarA = 0x00, VarB, VarC = 0x02, } #[rstest(input, expected, case(&hex!("00"), TestEnumDiscriminant::VarA), case(&hex!("01"), TestEnumDiscriminant::VarB), case(&hex!("02"), TestEnumDiscriminant::VarC), #[should_panic(expected = "Could not match enum variant id = 3 on enum `TestEnumDiscriminant`")] case(&hex!("03"), TestEnumDiscriminant::VarA), )] fn test_enum_discriminant(input: &[u8], expected: TestEnumDiscriminant) { let input = input.to_vec(); let ret_read = TestEnumDiscriminant::try_from(input.as_slice()).unwrap(); assert_eq!(expected, ret_read); let ret_write: Vec = ret_read.try_into().unwrap(); assert_eq!(input, ret_write); } #[test] fn test_enum_array_type() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "[u8; 3]")] enum TestEnumArray { #[deku(id = b"123")] VarA, #[deku(id = "[1,1,1]")] VarB, #[deku(id_pat = "_")] VarC((u8, u8, u8)), } let input = b"123".to_vec(); let ret_read = TestEnumArray::try_from(input.as_slice()).unwrap(); assert_eq!(TestEnumArray::VarA, ret_read); let ret_write: Vec = ret_read.try_into().unwrap(); assert_eq!(input.to_vec(), ret_write); let input = b"321".to_vec(); let ret_read = TestEnumArray::try_from(input.as_slice()).unwrap(); assert_eq!(TestEnumArray::VarC((b"3"[0], b"2"[0], b"1"[0])), ret_read); let ret_write: Vec = ret_read.try_into().unwrap(); assert_eq!(input.to_vec(), ret_write); } #[test] fn test_id_pat_with_id() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] pub struct DekuTest { my_id: u8, #[deku(ctx = "*my_id")] enum_from_id: MyEnum, } #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(ctx = "my_id: u8", id = "my_id")] pub enum MyEnum { #[deku(id_pat = "1..=2")] VariantA(u8), // Id type can differ from id_type #[deku(id_pat = "4..=5")] VariantDiffType(u16), #[deku(id_pat = "_")] VariantB, } let input = [0x01, 0x02]; let mut cursor = Cursor::new(input); let (_, v) = DekuTest::from_reader((&mut cursor, 0)).unwrap(); assert_eq!( v, DekuTest { my_id: 0x01, enum_from_id: MyEnum::VariantA(2) } ); assert_eq!(input, &*v.to_bytes().unwrap()); let input = [0x04, 0x02, 0xff]; let mut cursor = Cursor::new(input); let (_, v) = DekuTest::from_reader((&mut cursor, 0)).unwrap(); assert_eq!( v, DekuTest { my_id: 0x04, enum_from_id: MyEnum::VariantDiffType(0xff02) } ); assert_eq!(input, &*v.to_bytes().unwrap()); let input = [0x06]; let mut cursor = Cursor::new(input); let (_, v) = DekuTest::from_reader((&mut cursor, 0)).unwrap(); assert_eq!( v, DekuTest { my_id: 0x06, enum_from_id: MyEnum::VariantB } ); assert_eq!(input, &*v.to_bytes().unwrap()); } #[test] #[cfg(feature = "bits")] fn id_pat_with_id_bits() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8", bits = "2")] pub enum IdPatBits { #[deku(id = 1)] A(#[deku(bits = 6)] u8), #[deku(id_pat = "_")] B(#[deku(bits = 8)] u8), } let input = [0b1100_1111]; let mut cursor = Cursor::new(input); let (_, v) = IdPatBits::from_reader((&mut cursor, 0)).unwrap(); assert_eq!(v, IdPatBits::B(0b1100_1111)); assert_eq!(input, &*v.to_bytes().unwrap()); #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8", bits = "7")] pub enum IdPatBitsBigger { #[deku(id = 1)] A(#[deku(bits = 9)] u16), #[deku(id_pat = "_")] B(u16), } let input = [0b0000_0000, 0b0000_0001]; let mut cursor = Cursor::new(input); let (_, v) = IdPatBitsBigger::from_reader((&mut cursor, 0)).unwrap(); assert_eq!(v, IdPatBitsBigger::B(0b1_0000_0000)); assert_eq!(input, &*v.to_bytes().unwrap()); #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8", bits = "7")] pub enum IdPatBitsTuple { #[deku(id = 1)] A(#[deku(bits = 9)] u16), #[deku(id_pat = "_")] B((u8, u8)), } let input = [0b0000_0000, 0b0000_0001]; let mut cursor = Cursor::new(input); let (_, v) = IdPatBitsTuple::from_reader((&mut cursor, 0)).unwrap(); assert_eq!(v, IdPatBitsTuple::B((0, 1))); assert_eq!(input, &*v.to_bytes().unwrap()); } #[test] #[cfg(feature = "bits")] fn test_litbool_as_id() { use deku::prelude::*; #[derive(DekuRead, DekuWrite, Debug, PartialEq, Eq)] pub struct A { #[deku(bits = 1)] bit: bool, #[deku(ctx = "*bit")] var: Var, } #[derive(DekuRead, DekuWrite, Debug, PartialEq, Eq)] #[deku(id = "bit", ctx = "bit: bool")] pub enum Var { #[deku(id = false)] False(#[deku(bits = 15)] u16), #[deku(id = true)] True(#[deku(bits = 15)] u16), } let input = [0b1000_0000, 0xff]; let mut cursor = Cursor::new(input); let (_, v) = A::from_reader((&mut cursor, 0)).unwrap(); assert_eq!( v, A { bit: true, var: Var::True(0x7f01), } ); assert_eq!(input, &*v.to_bytes().unwrap()); let input = [0b0000_0000, 0xff]; let mut cursor = Cursor::new(input); let (_, v) = A::from_reader((&mut cursor, 0)).unwrap(); assert_eq!( v, A { bit: false, var: Var::False(0x7f01), } ); assert_eq!(input, &*v.to_bytes().unwrap()); } #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u16", id_endian = "big", endian = "little")] enum VariableEndian { #[deku(id = "0x01")] Little(u16), #[deku(id = "0x02")] Big { #[deku(endian = "big")] x: u16, }, } #[rstest(input, expected, case(&hex!("00010100"), VariableEndian::Little(1)), case(&hex!("00020100"), VariableEndian::Big{x: 256}) )] fn test_variable_endian_enum(input: &[u8], expected: VariableEndian) { let ret_read = VariableEndian::try_from(input).unwrap(); assert_eq!(expected, ret_read); let ret_write: Vec = ret_read.try_into().unwrap(); assert_eq!(input.to_vec(), ret_write); }