diff --git a/Cargo.lock b/Cargo.lock index 7c2e456dcc9..293dfaba4a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3273,6 +3273,7 @@ dependencies = [ "displaydoc", "nom", "num", + "paste", "thiserror", "unsigned-varint 0.7.2", ] @@ -3821,6 +3822,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pathdiff" version = "0.2.1" @@ -4194,7 +4201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.11.0", "proc-macro2 1.0.67", "quote 1.0.33", "syn 2.0.37", diff --git a/Cargo.toml b/Cargo.toml index f6738e2998e..02ffad3c484 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,6 +148,7 @@ num = "=0.4" num_enum = "0.7" paginate = "1.1" parking_lot = "0.12" +paste = "1.0" pbkdf2 = { version = "=0.12", features = ["simple"] } prometheus = "0.13" rand = "0.8" diff --git a/massa-serialization/Cargo.toml b/massa-serialization/Cargo.toml index 1b6b97fbe8a..2082997bf0f 100644 --- a/massa-serialization/Cargo.toml +++ b/massa-serialization/Cargo.toml @@ -12,3 +12,6 @@ thiserror = {workspace = true} nom = {workspace = true} unsigned-varint = {workspace = true, "features" = ["nom"]} num = {workspace = true} + +[dev-dependencies] +paste = {workspace = true} diff --git a/massa-serialization/src/lib.rs b/massa-serialization/src/lib.rs index 60f5778d575..26be9c2cc83 100644 --- a/massa-serialization/src/lib.rs +++ b/massa-serialization/src/lib.rs @@ -479,3 +479,247 @@ where .parse(buffer) } } + +#[cfg(test)] +mod tests { + use crate::{DeserializeError, Deserializer, Serializer}; + use num::rational::Ratio; + use paste::paste; + + // This macro creates a suite of tests for all types of numbers declared as parameters. Ths list of the + // tests for each type : + // - Test with a normal case that everything works + // - Test with a normal case but a more bigger number that everything works + // - Test with a number that is out of the range of the deserializer + // - Test to give an empty buffer to the deserializer + macro_rules! gen_test_varint { + ($($type:ident, $bs:ident, $ds:ident);*) => { + $( + paste! { + #[test] + fn []() { + let [< $type _serializer >] = super::$bs::new(); + let number = [<3 $type >]; + let mut buffer = Vec::new(); + [< $type _serializer >].serialize(&number, &mut buffer).expect(concat!("Failed to serialize ", stringify!($type), " 3")); + assert_eq!(buffer, vec![3]); + let [< $type _deserializer >] = super::$ds::new(std::ops::Bound::Included([<0 $type >]), std::ops::Bound::Included(number)); + let result = [< $type _deserializer >].deserialize::(&buffer); + assert!(result.is_ok()); + let (rest, value) = result.unwrap(); + assert!(rest.is_empty()); + assert_eq!(value, number); + } + + #[test] + fn []() { + let [< $type _serializer >] = super::$bs::new(); + let number = [<60_500 $type>]; + let mut buffer = Vec::new(); + [< $type _serializer >].serialize(&number, &mut buffer).expect(concat!("Failed to serialize ", stringify!($type), " 10_000_000")); + assert_eq!(buffer, vec![212, 216, 3]); + let [< $type _deserializer >] = super::$ds::new(std::ops::Bound::Included([<0 $type >]), std::ops::Bound::Included(number)); + let result = [< $type _deserializer >].deserialize::(&buffer); + assert!(result.is_ok()); + let (rest, value) = result.unwrap(); + assert!(rest.is_empty()); + assert_eq!(value, number); + } + + #[test] + fn []() { + let [< $type _serializer >] = super::$bs::new(); + let number = [<3 $type >]; + let mut buffer = Vec::new(); + [< $type _serializer >].serialize(&number, &mut buffer).expect(concat!("Failed to serialize ", stringify!($type), " 3")); + assert_eq!(buffer, vec![3]); + let [< $type _deserializer >] = super::$ds::new(std::ops::Bound::Included([<0 $type >]), std::ops::Bound::Excluded(number)); + let result = [< $type _deserializer >].deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(format!("{}", err), concat!("Parsing Error: Failed ", stringify!($type), " deserialization / Fail / Input: [3]\n")); + } + + #[test] + fn []() { + let buffer = vec![]; + let [< $type _deserializer >] = super::$ds::new(std::ops::Bound::Included([<0 $type >]), std::ops::Bound::Included($type::MAX)); + let result = [< $type _deserializer >].deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(format!("{}", err), concat!("Parsing Error: Failed ", stringify!($type), " deserialization / Fail / Input: []\n")); + } + } + )* + }; + } + + gen_test_varint!( + u16, U16VarIntSerializer, U16VarIntDeserializer; + u32, U32VarIntSerializer, U32VarIntDeserializer; + u64, U64VarIntSerializer, U64VarIntDeserializer + ); + + #[test] + fn test_u64_empty_vec() { + let buffer = vec![]; + let u64_deserializer = super::U64VarIntDeserializer::new( + std::ops::Bound::Included(0), + std::ops::Bound::Included(3), + ); + let result = u64_deserializer.deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!( + format!("{}", err), + "Parsing Error: Failed u64 deserialization / Fail / Input: []\n" + ); + } + + #[test] + fn test_option_serializer_value_works() { + let option_serializer = super::OptionSerializer::new(super::U64VarIntSerializer::new()); + let mut buffer = Vec::new(); + option_serializer + .serialize(&Some(3u64), &mut buffer) + .expect("Failed to serialize Some(3)"); + assert_eq!(buffer, vec![b'1', 3]); + let option_deserializer = + super::OptionDeserializer::new(super::U64VarIntDeserializer::new( + std::ops::Bound::Included(0), + std::ops::Bound::Included(3), + )); + let result = option_deserializer.deserialize::(&buffer); + assert!(result.is_ok()); + let (rest, value) = result.unwrap(); + assert!(rest.is_empty()); + assert_eq!(value, Some(3u64)); + } + + #[test] + fn test_option_serializer_none_works() { + let option_serializer = super::OptionSerializer::new(super::U64VarIntSerializer::new()); + let mut buffer = Vec::new(); + option_serializer + .serialize(&None, &mut buffer) + .expect("Failed to serialize None"); + assert_eq!(buffer, vec![b'0']); + let option_deserializer = + super::OptionDeserializer::new(super::U64VarIntDeserializer::new( + std::ops::Bound::Included(0), + std::ops::Bound::Included(3), + )); + let result = option_deserializer.deserialize::(&buffer); + assert!(result.is_ok()); + let (rest, value) = result.unwrap(); + assert!(rest.is_empty()); + assert_eq!(value, None); + } + + #[test] + fn test_option_bad_serialized_vec() { + let buffer = vec![2]; + let option_deserializer = + super::OptionDeserializer::new(super::U64VarIntDeserializer::new( + std::ops::Bound::Included(0), + std::ops::Bound::Included(3), + )); + let result = option_deserializer.deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(format!("{}", err), "Parsing Error: Option<_> deserializer failed / Alternative / Some(_) / Tag / Input: [2]\n"); + } + + #[test] + fn test_option_empty_vec() { + let buffer = vec![]; + let option_deserializer = + super::OptionDeserializer::new(super::U64VarIntDeserializer::new( + std::ops::Bound::Included(0), + std::ops::Bound::Included(3), + )); + let result = option_deserializer.deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(format!("{}", err), "Parsing Error: Option<_> deserializer failed / Alternative / Some(_) / Tag / Input: []\n"); + } + + #[test] + fn test_bool_serializer_deserializer_works() { + let bool_serializer = super::BoolSerializer::new(); + let mut buffer = Vec::new(); + bool_serializer + .serialize(&true, &mut buffer) + .expect("Failed to serialize true"); + assert_eq!(buffer, vec![1]); + let bool_deserializer = super::BoolDeserializer::new(); + let result = bool_deserializer.deserialize::(&buffer); + assert!(result.is_ok()); + let (rest, value) = result.unwrap(); + assert!(rest.is_empty()); + assert_eq!(value, true); + } + + #[test] + fn test_bool_bad_serialized_vec() { + let buffer = vec![2]; + let bool_deserializer = super::BoolDeserializer::new(); + let result = bool_deserializer.deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!( + format!("{}", err), + "Parsing Error: Failed bool deserialization / Fail / Input: [2]\n" + ); + } + + #[test] + fn test_bool_empty_vec() { + let buffer = vec![]; + let bool_deserializer = super::BoolDeserializer::new(); + let result = bool_deserializer.deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!( + format!("{}", err), + "Parsing Error: Failed bool deserialization / Fail / Input: []\n" + ); + } + + #[test] + fn test_ratio_serializer_deserializer_works() { + let ratio_serializer = super::RatioSerializer::new(super::U64VarIntSerializer::new()); + let mut buffer = Vec::new(); + ratio_serializer + .serialize(&Ratio::new(3u64, 4u64), &mut buffer) + .expect("Failed to serialize Ratio(3, 4)"); + assert_eq!(buffer, vec![3, 4]); + let ratio_deserializer = super::RatioDeserializer::new(super::U64VarIntDeserializer::new( + std::ops::Bound::Included(0), + std::ops::Bound::Included(4), + )); + let result = ratio_deserializer.deserialize::(&buffer); + assert!(result.is_ok()); + let (rest, value) = result.unwrap(); + assert!(rest.is_empty()); + assert_eq!(value, Ratio::new(3u64, 4u64)); + } + + #[test] + fn test_ratio_serializer_deserializer_bad_limits() { + let ratio_serializer = super::RatioSerializer::new(super::U64VarIntSerializer::new()); + let mut buffer = Vec::new(); + ratio_serializer + .serialize(&Ratio::new(3u64, 4u64), &mut buffer) + .expect("Failed to serialize Ratio(3, 4)"); + assert_eq!(buffer, vec![3, 4]); + let ratio_deserializer = super::RatioDeserializer::new(super::U64VarIntDeserializer::new( + std::ops::Bound::Included(0), + std::ops::Bound::Included(3), + )); + let result = ratio_deserializer.deserialize::(&buffer); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(format!("{}", err), "Parsing Error: Ratio<_> deserializer failed / denom deser failed / Failed u64 deserialization / Fail / Input: [4]\n"); + } +}