use alloc::vec::Vec;
use flagset::FlagSet;
use proptest::prelude::*;
use super::EncodationType;
use crate::data::{decode_data, encode_data, encode_data_unpadded_len};
use crate::encodation::planner::optimize_cost;
use crate::symbol_size::{SymbolList, SymbolSize};
const MODES: [EncodationType; 6] = [
EncodationType::Ascii,
EncodationType::C40,
EncodationType::Text,
EncodationType::X12,
EncodationType::Edifact,
EncodationType::Base256,
];
fn modes_from_mask(mask: u8) -> FlagSet<EncodationType> {
let mut set = FlagSet::from(EncodationType::Ascii);
for (i, mode) in MODES.iter().enumerate().skip(1) {
if mask & (1 << i) != 0 {
set |= *mode;
}
}
set
}
fn data_strategy() -> impl Strategy<Value = Vec<u8>> {
prop_oneof![
proptest::collection::vec(any::<u8>(), 0..64), proptest::collection::vec(0x20u8..0x7f, 0..96), proptest::collection::vec(0x30u8..0x3a, 0..96), proptest::collection::vec(0x41u8..0x5b, 0..96), proptest::collection::vec(0x20u8..0x5f, 0..96), ]
}
fn symbols_strategy() -> impl Strategy<Value = SymbolList> {
prop_oneof![
Just(SymbolList::default()),
Just(SymbolList::with_extended_rectangles()),
Just(SymbolList::default().enforce_square()),
any::<prop::sample::Index>().prop_map(|idx| {
let all: Vec<SymbolSize> = enum_iterator::all::<SymbolSize>().collect();
SymbolList::from(all[idx.index(all.len())])
}),
]
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(1024))]
#[test]
fn planner_cost_matches_encoder_length(
data in data_strategy(),
mask in any::<u8>(),
symbols in symbols_strategy(),
) {
let modes = modes_from_mask(mask);
if let Some(actual) = encode_data_unpadded_len(&data, &symbols, modes) {
let predicted = optimize_cost(&data, 0, EncodationType::Ascii, &symbols, modes);
prop_assert_eq!(
predicted, Some(actual),
"planner predicted {:?} codewords but encoder produced {}; data={:?} modes={:?}",
predicted, actual, data, modes,
);
}
}
#[test]
fn more_modes_never_increase_size(data in data_strategy(), mask in any::<u8>()) {
let symbols = SymbolList::default();
let size = |modes| {
encode_data(&data, &symbols, None, modes, false)
.ok()
.map(|(_, symbol)| symbol.num_data_codewords())
};
let all = size(EncodationType::all());
let subset = size(modes_from_mask(mask));
if let (Some(all), Some(subset)) = (all, subset) {
prop_assert!(
all <= subset,
"all modes gave {} codewords, subset {:?} gave {}; data={:?}",
all, modes_from_mask(mask), subset, data,
);
}
}
#[test]
fn round_trip(data in data_strategy(), mask in any::<u8>()) {
let modes = modes_from_mask(mask);
if let Ok((codewords, _)) = encode_data(&data, &SymbolList::default(), None, modes, false) {
let decoded = decode_data(&codewords);
prop_assert!(decoded.is_ok(), "decode failed: {:?} for data {:?}", decoded, data);
prop_assert_eq!(decoded.unwrap(), data);
}
}
}