#![allow(clippy::unwrap_used, clippy::expect_used)]
use chronix_encoding::{ColumnDecoder, EncodedBlock, EncodingType};
use proptest::prelude::*;
const ENCODINGS: &[EncodingType] = EncodingType::ALL;
#[test]
fn every_encoding_is_covered() {
assert!(
ENCODINGS.len() >= 25,
"the codec list shrank to {} — a decoder removed from `EncodingType` is a \
format change, not a refactor",
ENCODINGS.len()
);
assert!(
ENCODINGS.contains(&EncodingType::DecimalI128),
"the exact-decimal codec must be fuzzed like every other"
);
let mut seen: Vec<String> = ENCODINGS.iter().map(|e| format!("{e:?}")).collect();
seen.sort();
seen.dedup();
assert_eq!(
seen.len(),
ENCODINGS.len(),
"duplicate entry in `EncodingType::ALL`"
);
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(256))]
#[test]
fn no_decoder_panics_on_arbitrary_bytes(data in prop::collection::vec(any::<u8>(), 0..512)) {
for &encoding in ENCODINGS {
let block = EncodedBlock { encoding, payload: data.clone() };
let _ = ColumnDecoder::decode(&block);
}
}
#[test]
fn decode_bytes_does_not_panic_on_arbitrary_bytes(
data in prop::collection::vec(any::<u8>(), 0..512)
) {
let _ = ColumnDecoder::decode_bytes(&data);
}
#[test]
fn no_decoder_panics_behind_a_plausible_header(
count in 1u32..64,
tail in prop::collection::vec(any::<u8>(), 0..256),
) {
let mut payload = Vec::with_capacity(4 + tail.len());
payload.extend_from_slice(&count.to_le_bytes());
payload.extend_from_slice(&tail);
for &encoding in ENCODINGS {
let block = EncodedBlock { encoding, payload: payload.clone() };
let _ = ColumnDecoder::decode(&block);
}
}
#[test]
fn no_decoder_panics_on_hostile_headers(
count in prop_oneof![Just(0u32), Just(1), Just(u32::MAX), Just(u32::MAX / 2), any::<u32>()],
width in prop_oneof![Just(0u8), Just(1), Just(64), Just(65), Just(255), any::<u8>()],
tail in prop::collection::vec(any::<u8>(), 0..64),
) {
let mut payload = Vec::with_capacity(8 + tail.len());
payload.extend_from_slice(&count.to_le_bytes());
payload.push(width);
payload.extend_from_slice(&tail);
for &encoding in ENCODINGS {
let block = EncodedBlock { encoding, payload: payload.clone() };
let _ = ColumnDecoder::decode(&block);
}
}
}
#[test]
fn truncating_a_valid_block_never_panics() {
let values: Vec<f64> = (0..1_000).map(|i| f64::from(i) * 0.25).collect();
let encoded = chronix_encoding::AlpEncoder::encode(&values).unwrap();
for cut in 0..encoded.len() {
let block = EncodedBlock {
encoding: EncodingType::Alp,
payload: encoded[..cut].to_vec(),
};
let _ = ColumnDecoder::decode(&block);
}
}
#[test]
fn single_bit_flips_never_panic() {
let values: Vec<f64> = (0..256).map(|i| f64::from(i) * 0.01).collect();
let encoded = chronix_encoding::AlpEncoder::encode(&values).unwrap();
for byte in 0..encoded.len() {
for bit in 0..8 {
let mut corrupted = encoded.clone();
corrupted[byte] ^= 1 << bit;
let block = EncodedBlock {
encoding: EncodingType::Alp,
payload: corrupted,
};
let _ = ColumnDecoder::decode(&block);
}
}
}