#![allow(
clippy::unreadable_literal,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::should_panic_without_expect,
clippy::items_after_statements,
clippy::match_same_arms,
clippy::comparison_chain
)]
use multi_codec::Codec;
use multi_trait::TryDecodeFrom;
use proptest::prelude::*;
#[test]
fn test_encode_decode_roundtrip() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let encoded: Vec<u8> = codec.into();
let (decoded, remaining) = Codec::try_decode_from(&encoded).unwrap();
prop_assert_eq!(codec, decoded);
prop_assert!(remaining.is_empty());
let decoded_code: u64 = decoded.into();
prop_assert_eq!(code, decoded_code);
}
});
}
#[test]
fn test_str_conversion_roundtrip() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let name = codec.as_str();
let decoded = Codec::try_from(name).unwrap();
prop_assert_eq!(codec, decoded);
}
});
}
#[test]
fn test_code_bidirectional() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let code2: u64 = codec.into();
prop_assert_eq!(code, code2);
prop_assert_eq!(code, codec.code());
}
});
}
#[test]
fn test_invalid_codes_rejected() {
proptest!(|(code in 0u64..=u64::MAX)| {
if let Ok(codec) = Codec::try_from(code) {
let code2: u64 = codec.into();
prop_assert_eq!(code, code2);
} else {
}
});
}
#[test]
fn test_str_never_empty() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let name = codec.as_str();
prop_assert!(!name.is_empty());
prop_assert!(!name.is_empty());
}
});
}
#[test]
fn test_debug_contains_name_and_code() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let debug = format!("{codec:?}");
let name = codec.as_str();
prop_assert!(debug.contains(name));
prop_assert!(debug.contains("0x"));
}
});
}
#[test]
fn test_display_equals_as_str() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let display = format!("{codec}");
let as_str = codec.as_str();
prop_assert_eq!(display, as_str);
}
});
}
#[test]
fn test_deterministic_behavior() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec1) = Codec::try_from(code) {
let codec2 = Codec::try_from(code).unwrap();
prop_assert_eq!(codec1, codec2);
prop_assert_eq!(codec1.code(), codec2.code());
prop_assert_eq!(codec1.as_str(), codec2.as_str());
let enc1: Vec<u8> = codec1.into();
let enc2: Vec<u8> = codec2.into();
prop_assert_eq!(enc1, enc2);
}
});
}
#[test]
fn test_hash_consistency() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let mut hasher1 = DefaultHasher::new();
codec.hash(&mut hasher1);
let hash1 = hasher1.finish();
let mut hasher2 = DefaultHasher::new();
codec.hash(&mut hasher2);
let hash2 = hasher2.finish();
prop_assert_eq!(hash1, hash2);
}
});
}
#[test]
fn test_ordering_by_code() {
proptest!(|(code1 in 0u64..=0x0FFFFFFF, code2 in 0u64..=0x0FFFFFFF)| {
if let (Ok(codec1), Ok(codec2)) = (Codec::try_from(code1), Codec::try_from(code2)) {
if code1 < code2 {
prop_assert!(codec1 < codec2);
} else if code1 > code2 {
prop_assert!(codec1 > codec2);
} else {
prop_assert_eq!(codec1, codec2);
}
}
});
}
#[test]
fn test_negative_always_fails() {
proptest!(|(value in i64::MIN..0i64)| {
let result = Codec::try_from(value);
prop_assert!(result.is_err());
});
}
#[test]
fn test_nonnegative_signed_matches_unsigned() {
proptest!(|(value in 0i64..=0x0FFFFFFF)| {
let from_signed = Codec::try_from(value);
let from_unsigned = Codec::try_from(value as u64);
match (from_signed, from_unsigned) {
(Ok(c1), Ok(c2)) => prop_assert_eq!(c1, c2),
(Err(_), Err(_)) => {}, _ => prop_assert!(false, "Signed and unsigned should agree"),
}
});
}
#[test]
fn test_encoded_length_bounded() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let encoded: Vec<u8> = codec.into();
prop_assert!(!encoded.is_empty());
prop_assert!(encoded.len() <= 10); }
});
}
#[test]
fn test_decode_consumes_exact_bytes() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let encoded: Vec<u8> = codec.into();
let original_len = encoded.len();
let (decoded, remaining) = Codec::try_decode_from(&encoded).unwrap();
prop_assert_eq!(codec, decoded);
prop_assert_eq!(remaining.len(), 0);
prop_assert_eq!(original_len, encoded.len() - remaining.len());
}
});
}
#[test]
fn test_decode_with_trailing_data() {
proptest!(|(code in 0u64..=0x0FFFFFFF, trailing in prop::collection::vec(any::<u8>(), 1..20))| {
if let Ok(codec) = Codec::try_from(code) {
let mut encoded: Vec<u8> = codec.into();
encoded.extend_from_slice(&trailing);
let (decoded, remaining) = Codec::try_decode_from(&encoded).unwrap();
prop_assert_eq!(codec, decoded);
prop_assert_eq!(remaining, trailing.as_slice());
}
});
}
#[cfg(feature = "serde")]
mod serde_props {
use super::*;
#[test]
fn test_json_roundtrip() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let json = serde_json::to_string(&codec).unwrap();
let decoded: Codec = serde_json::from_str(&json).unwrap();
prop_assert_eq!(codec, decoded);
}
});
}
#[test]
fn test_cbor_roundtrip() {
proptest!(|(code in 0u64..=0x0FFFFFFF)| {
if let Ok(codec) = Codec::try_from(code) {
let bytes = serde_cbor::to_vec(&codec).unwrap();
let decoded: Codec = serde_cbor::from_slice(&bytes).unwrap();
prop_assert_eq!(codec, decoded);
}
});
}
}