use rand::{Rng, SeedableRng, rngs::StdRng};
use revision::prelude::*;
const SEED: u64 = 0xDEADBEEF_CAFEBABE;
const SAMPLES: usize = 10_000;
#[revisioned(revision(1, optimised))]
#[derive(Debug, Clone, PartialEq)]
struct OptStruct {
a: u32,
b: u32,
c: u64,
d: i32,
}
#[revisioned(revision(1, optimised, indexed_struct))]
#[derive(Debug, Clone, PartialEq)]
struct IndexedStruct {
first: u32,
second: String,
third: i64,
fourth: bool,
}
#[revisioned(revision(1, optimised))]
#[derive(Debug, Clone, PartialEq)]
enum OptEnum {
#[revision(size = "inline")]
Unit,
#[revision(size = "fixed(8)")]
Bytes([u8; 8]),
#[revision(size = "varlen")]
Text(String),
}
fn random_string(rng: &mut StdRng) -> String {
let len = rng.random_range(0..32);
(0..len).map(|_| rng.random_range(b'a'..=b'z') as char).collect()
}
fn gen_opt_struct(rng: &mut StdRng) -> OptStruct {
OptStruct {
a: rng.random(),
b: rng.random(),
c: rng.random(),
d: rng.random(),
}
}
fn gen_indexed_struct(rng: &mut StdRng) -> IndexedStruct {
IndexedStruct {
first: rng.random(),
second: random_string(rng),
third: rng.random(),
fourth: rng.random(),
}
}
fn gen_opt_enum(rng: &mut StdRng) -> OptEnum {
match rng.random_range(0..3) {
0 => OptEnum::Unit,
1 => OptEnum::Bytes(rng.random()),
_ => OptEnum::Text(random_string(rng)),
}
}
fn check_round_trip<T>(value: &T)
where
T: SerializeRevisioned + DeserializeRevisioned + PartialEq + std::fmt::Debug,
{
let bytes = revision::to_vec(value).expect("encode");
let decoded: T = revision::from_slice(&bytes).expect("decode");
assert_eq!(&decoded, value, "round-trip mismatch");
}
fn check_skip_advances_exact_bytes<T>(value: &T)
where
T: SerializeRevisioned + SkipRevisioned,
{
let bytes = revision::to_vec(value).expect("encode");
let original_len = bytes.len();
let mut cursor: &[u8] = &bytes;
<T as SkipRevisioned>::skip_revisioned(&mut cursor).expect("skip");
assert_eq!(
original_len - cursor.len(),
original_len,
"skip should advance through every byte the encoder wrote"
);
assert!(cursor.is_empty(), "skip should consume the entire encoded value");
}
#[test]
fn property_round_trip_opt_struct() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
check_round_trip(&gen_opt_struct(&mut rng));
}
}
#[test]
fn property_skip_advances_exact_for_opt_struct() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
check_skip_advances_exact_bytes(&gen_opt_struct(&mut rng));
}
}
#[test]
fn property_round_trip_indexed_struct() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
check_round_trip(&gen_indexed_struct(&mut rng));
}
}
#[test]
fn property_skip_advances_exact_for_indexed_struct() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
check_skip_advances_exact_bytes(&gen_indexed_struct(&mut rng));
}
}
#[test]
fn property_round_trip_opt_enum() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
check_round_trip(&gen_opt_enum(&mut rng));
}
}
#[test]
fn property_skip_advances_exact_for_opt_enum() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
check_skip_advances_exact_bytes(&gen_opt_enum(&mut rng));
}
}
#[test]
fn property_walker_discriminant_matches_variant() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
let v = gen_opt_enum(&mut rng);
let bytes = revision::to_vec(&v).unwrap();
let mut r: &[u8] = &bytes;
let w = OptEnum::walk_revisioned(&mut r).unwrap();
match &v {
OptEnum::Unit => {
assert!(w.is_unit());
assert!(!w.is_bytes());
assert!(!w.is_text());
}
OptEnum::Bytes(_) => {
assert!(w.is_bytes());
assert!(!w.is_unit());
assert!(!w.is_text());
}
OptEnum::Text(_) => {
assert!(w.is_text());
assert!(!w.is_unit());
assert!(!w.is_bytes());
}
}
}
}
#[test]
fn property_walker_decode_variant_returns_inner_value() {
let mut rng = StdRng::seed_from_u64(SEED);
for _ in 0..SAMPLES {
let v = gen_opt_enum(&mut rng);
let bytes = revision::to_vec(&v).unwrap();
let mut r: &[u8] = &bytes;
let w = OptEnum::walk_revisioned(&mut r).unwrap();
match v {
OptEnum::Unit => {
w.decode_unit().expect("Unit decode");
}
OptEnum::Bytes(b) => {
let inner = w.decode_bytes().expect("Bytes decode");
assert_eq!(inner, b);
}
OptEnum::Text(s) => {
let inner = w.decode_text().expect("Text decode");
assert_eq!(inner, s);
}
}
}
}