use super::*;
use crate::varint::VarInt;
use bolero::check;
use core::mem::size_of;
use s2n_codec::{
assert_codec_round_trip_bytes, assert_codec_round_trip_value, DecoderBuffer, EncoderBuffer,
};
#[test]
#[cfg_attr(miri, ignore)] fn round_trip_bytes_test() {
check!().for_each(|input| {
assert_codec_round_trip_bytes!(VarInt, input);
});
}
#[test]
#[cfg_attr(kani, kani::proof, kani::unwind(10))]
fn round_trip_values_test() {
check!().with_type().cloned().for_each(|v| {
if let Ok(v) = VarInt::new(v) {
assert_codec_round_trip_value!(VarInt, v);
} else {
assert!(v > MAX_VARINT_VALUE);
}
})
}
#[test]
#[cfg_attr(miri, ignore)] fn table_snapshot_test() {
use insta::assert_debug_snapshot;
assert_debug_snapshot!("max_value", MAX_VARINT_VALUE);
for i in [0, 1, 5, 6, 13, 14, 29, 30, 61] {
assert_debug_snapshot!(format!("table_2_pow_{}_", i), read_table(2u64.pow(i)));
}
}
macro_rules! sequence_test {
($name:ident($input:expr, $expected:expr)) => {
#[test]
fn $name() {
use s2n_codec::assert_codec_round_trip_value;
let input = $input;
let expected = VarInt::new($expected).unwrap();
let actual_bytes = assert_codec_round_trip_value!(VarInt, expected);
assert_eq!(&input[..], &actual_bytes[..]);
}
};
}
sequence_test!(eight_byte_sequence_example(
[0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c],
151_288_809_941_952_652
));
sequence_test!(four_byte_sequence_example(
[0x9d, 0x7f, 0x3e, 0x7d],
494_878_333
));
sequence_test!(two_byte_sequence_example([0x7b, 0xbd], 15293));
sequence_test!(one_byte_sequence_example([0x25], 37));
#[test]
#[cfg_attr(miri, ignore)]
#[cfg_attr(kani, kani::proof, kani::unwind(3))]
fn one_byte_sequence_test() {
bolero::check!()
.with_type()
.cloned()
.for_each(|mut first_byte: u8| {
first_byte &= 0x3f;
let byte_sequence = [first_byte];
let expected = VarInt::new(first_byte as u64).unwrap();
let actual_bytes = assert_codec_round_trip_value!(VarInt, expected);
assert_eq!(&byte_sequence[..], &actual_bytes[..]);
});
}
#[test]
#[cfg_attr(miri, ignore)]
#[cfg_attr(kani, kani::proof, kani::unwind(4))]
fn two_byte_sequence_test() {
let g = gen::<(u8, u8)>().filter_gen(|(_, b)| *b > 63);
bolero::check!()
.with_generator(g)
.cloned()
.for_each(|(mut first_byte, second_byte)| {
first_byte = (first_byte & 0x3f) | 0x40;
let byte_sequence = [first_byte, second_byte];
let expected_val = u16::from_be_bytes([first_byte & 0x3f, second_byte]);
assert!(expected_val <= 16383);
let expected = VarInt::new(expected_val as u64).unwrap();
let actual_bytes = assert_codec_round_trip_value!(VarInt, expected);
assert_eq!(&byte_sequence[..], &actual_bytes[..]);
});
}
#[test]
#[cfg_attr(miri, ignore)]
#[cfg_attr(kani, kani::proof, kani::unwind(10))]
fn four_byte_sequence_test() {
let g = gen::<(u8, u8, u8)>().filter_gen(|(_, _, c)| *c > 0x3f);
bolero::check!().with_generator(g).cloned().for_each(
|(mut first_byte, second_byte, third_byte)| {
first_byte = (first_byte & 0x3f) | 0x80;
let byte_sequence = [first_byte, second_byte, third_byte, 0xff];
let expected_val: u32 =
u32::from_be_bytes([first_byte & 0x3f, second_byte, third_byte, 0xff]);
assert!(expected_val <= 1073741823);
let expected = VarInt::new(expected_val as u64).unwrap();
let actual_bytes = assert_codec_round_trip_value!(VarInt, expected);
assert_eq!(&byte_sequence[..], &actual_bytes[..]);
},
);
}
#[test]
#[cfg_attr(miri, ignore)]
#[cfg_attr(kani, kani::proof, kani::unwind(10))]
fn eight_byte_sequence_test() {
let g = gen::<(u8, u8, u8, u8)>().filter_gen(|(_, _, _, d)| *d > 0x3f);
bolero::check!().with_generator(g).cloned().for_each(
|(mut first_byte, second_byte, third_byte, fourth_byte)| {
first_byte = (first_byte & 0x3f) | 0xc0;
let byte_sequence = [
first_byte,
second_byte,
third_byte,
fourth_byte,
0xff,
0xff,
0xff,
0xff,
];
let expected_val: u64 = u64::from_be_bytes([
first_byte & 0x3f,
second_byte,
third_byte,
fourth_byte,
0xff,
0xff,
0xff,
0xff,
]);
assert!(expected_val <= 4611686018427387903);
let expected = VarInt::new(expected_val).unwrap();
let actual_bytes = assert_codec_round_trip_value!(VarInt, expected);
assert_eq!(&byte_sequence[..], &actual_bytes[..]);
},
);
}
fn test_update(initial: VarInt, expected: VarInt, encoder: &mut EncoderBuffer) {
encoder.set_position(0);
initial.encode_updated(expected, encoder);
let decoder = DecoderBuffer::new(encoder.as_mut_slice());
let (actual, _) = decoder.decode::<VarInt>().unwrap();
assert_eq!(expected, actual);
}
#[test]
fn encode_updated_test() {
let mut buffer = [0u8; size_of::<VarInt>()];
let mut encoder = EncoderBuffer::new(&mut buffer);
let initial = VarInt::from_u16(1 << 14);
encoder.encode(&initial);
test_update(initial, VarInt::from_u32(0), &mut encoder);
test_update(initial, VarInt::from_u32(1 << 14), &mut encoder);
test_update(initial, VarInt::from_u32(1 << 29), &mut encoder);
}
#[test]
#[should_panic]
fn encode_updated_invalid_test() {
let mut buffer = [0u8; size_of::<VarInt>()];
let mut encoder = EncoderBuffer::new(&mut buffer);
let initial = VarInt::from_u16(1 << 14);
encoder.encode(&initial);
test_update(initial, VarInt::from_u32(1 << 30), &mut encoder);
}
#[test]
#[cfg_attr(kani, kani::proof, kani::unwind(5))]
fn table_differential_test() {
check!()
.with_generator(0..=MAX_VARINT_VALUE)
.cloned()
.for_each(|v| {
let actual = read_table(v);
assert_eq!(actual.1, encoding_size(v));
#[allow(clippy::match_overlapping_arm)]
let expected = match v {
0..=63 => (0b00, 1, 6),
0..=16383 => (0b01, 2, 14),
0..=1073741823 => (0b10, 4, 30),
0..=4611686018427387903 => (0b11, 8, 62),
_ => unreachable!(),
};
assert_eq!(actual, expected);
})
}
#[test]
#[cfg_attr(kani, kani::proof, kani::unwind(5))]
fn checked_ops_test() {
check!().with_type().cloned().for_each(|(a, b)| {
if let (Ok(a_v), Ok(b_v)) = (VarInt::new(a), VarInt::new(b)) {
macro_rules! checked {
($checked:ident, $op:tt) => {{
if let Some(res) = a_v.$checked(b_v) {
assert_eq!(Some(res.as_u64()), a.$checked(b));
assert_eq!(res, a_v $op b_v);
} else {
let expected = a.$checked(b).and_then(|v| VarInt::new(v).ok());
assert!(expected.is_none());
}
}};
}
checked!(checked_add, +);
checked!(checked_sub, -);
#[cfg(any(not(kani), kani_slow))]
checked!(checked_mul, *);
#[cfg(any(not(kani), kani_slow))]
checked!(checked_div, /);
macro_rules! saturating {
($name:ident) => {{
let actual = a_v.$name(b_v);
let expected = VarInt::new(a.$name(b)).unwrap_or(VarInt::MAX);
assert_eq!(actual, expected);
}};
}
saturating!(saturating_add);
#[cfg(any(not(kani), kani_slow))]
saturating!(saturating_sub);
#[cfg(any(not(kani), kani_slow))]
saturating!(saturating_mul);
}
})
}