use super::*;
type Utc = UtcDateTime;
type Datetime = PrimitiveDateTime;
#[derive(Debug, Clone, Copy)]
struct DurationWrapper(Duration);
impl quickcheck::Arbitrary for DurationWrapper {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let d: core::time::Duration = quickcheck::Arbitrary::arbitrary(g);
Self(Duration::new(d.as_secs() as i64, d.subsec_nanos() as i32))
}
}
#[quickcheck_macros::quickcheck]
fn fuzzy_duration(value: DurationWrapper) -> bool {
let value = value.0;
let encoded = encode_duration(&value);
if encoded.len() != encoded_duration_len(&value).get()
|| (encoded.len() > <Duration>::MAX_ENCODED_LEN.get())
{
return false;
}
let Some(consumed) = crate::consume_varint_checked(&encoded) else {
return false;
};
if consumed.get() != encoded.len() {
return false;
}
if let Ok((bytes_read, decoded)) = decode_duration(&encoded) {
value == decoded && encoded.len() == bytes_read.get()
} else {
false
}
}
#[quickcheck_macros::quickcheck]
fn fuzzy_duration_varint(value: DurationWrapper) -> bool {
let value = value.0;
let mut buf = [0; <Duration>::MAX_ENCODED_LEN.get()];
let Ok(encoded_len) = value.encode(&mut buf) else {
return false;
};
if encoded_len != value.encoded_len() || (value.encoded_len() > <Duration>::MAX_ENCODED_LEN) {
return false;
}
let Some(consumed) = crate::consume_varint_checked(&buf) else {
return false;
};
if consumed != encoded_len {
return false;
}
if let Ok((bytes_read, decoded)) = <Duration>::decode(&buf) {
value == decoded && encoded_len == bytes_read
} else {
false
}
}
fuzzy!(@varing_ref(Date, Datetime, Time, Utc));
fuzzy!(@varint(Date, PrimitiveDateTime, Time, UtcDateTime));
#[test]
fn decode_rejects_nanos_overflow_without_panic() {
let merged = time_utils::secs_and_subsec_nanos_to_merged(i64::MAX, 1_000_000_000);
let encoded = crate::encode_u128_varint(merged);
let result = std::panic::catch_unwind(move || decode_duration(&encoded));
assert!(matches!(result, Ok(Err(_))));
let normal = Duration::new(42, 123_456_789);
let enc = encode_duration(&normal);
let (read, decoded) = decode_duration(&enc).unwrap();
assert_eq!(decoded, normal);
assert_eq!(read.get(), enc.len());
}
#[test]
fn decode_rejects_out_of_range_bits_without_panic() {
let encoded = crate::encode_u128_varint(1u128 << 96);
assert!(decode_duration(&encoded).is_err());
let normal = Duration::new(42, 123_456_789);
let enc = encode_duration(&normal);
let (read, decoded) = decode_duration(&enc).unwrap();
assert_eq!(decoded, normal);
assert_eq!(read.get(), enc.len());
}
#[test]
fn decode_rejects_non_canonical_time() {
let encoded = crate::encode_u64_varint(1u64 << 48);
assert!(decode_time(&encoded).is_err());
let value = Time::from_hms_nano(12, 34, 56, 789_000_000).unwrap();
let enc = encode_time(&value);
let (read, decoded) = decode_time(&enc).unwrap();
assert_eq!(decoded, value);
assert_eq!(read.get(), enc.len());
}
#[test]
fn decode_rejects_non_canonical_datetime() {
let valid = time_utils::date_time_to_merged(2024, 6, 15, 12, 34, 56, 789_000_000);
let bad = valid | (1i128 << 100);
let encoded = crate::encode_i128_varint(bad);
assert!(decode_datetime(&encoded).is_err());
let date = Date::from_calendar_date(2024, Month::June, 15).unwrap();
let time = Time::from_hms_nano(12, 34, 56, 789_000_000).unwrap();
let value = PrimitiveDateTime::new(date, time);
let enc = encode_datetime(&value);
let (read, decoded) = decode_datetime(&enc).unwrap();
assert_eq!(decoded, value);
assert_eq!(read.get(), enc.len());
}
#[test]
fn decode_rejects_sign_mismatch_duration() {
let merged = time_utils::secs_and_subsec_nanos_to_merged(1, -1);
assert!(decode_duration(&crate::encode_u128_varint(merged)).is_err());
let merged = time_utils::secs_and_subsec_nanos_to_merged(-1, 1);
assert!(decode_duration(&crate::encode_u128_varint(merged)).is_err());
for value in [
Duration::new(0, -5),
Duration::new(5, 0),
Duration::new(3, 500_000_000),
Duration::new(-5, -5),
] {
let enc = encode_duration(&value);
let (read, decoded) = decode_duration(&enc).unwrap();
assert_eq!(decoded, value);
assert_eq!(read.get(), enc.len());
}
}
#[test]
fn decode_date_round_trip() {
let value = Date::from_calendar_date(2024, Month::June, 15).unwrap();
let enc = encode_date(&value);
let (read, decoded) = decode_date(&enc).unwrap();
assert_eq!(decoded, value);
assert_eq!(read.get(), enc.len());
}