use super::*;
use std::cmp::Ordering;
fn roundtrip(s: &[u8], prefix: usize) {
let mut blob = vec![0xEEu8; prefix];
let st = encode_german_string(s, &mut blob);
assert_eq!(
try_decode_german_string(&st, &blob).as_deref(),
Some(s),
"try_decode roundtrip failed for len {} at prefix {prefix}",
s.len(),
);
assert_eq!(
german_string_content(&st, &blob),
s,
"the two readers disagree at len {} prefix {prefix}",
s.len(),
);
if s.len() > SHORT_STRING_THRESHOLD {
assert_eq!(
read_u64_le(&st, 8) as usize,
prefix,
"a heap payload must sit past the arena's existing bytes"
);
assert_eq!(blob.len(), prefix + s.len(), "the payload is appended whole");
} else {
assert_eq!(blob.len(), prefix, "an inline payload must not touch the arena");
}
assert!(german_string_cell_ok(&st, &blob), "canonical cell rejected: {s:?}");
let short = s.len() <= SHORT_STRING_THRESHOLD;
assert_eq!(canonical_short_cell(&st), short.then_some(st));
assert_eq!(german_string_short_ascii(&st), short && s.is_ascii());
let mut shifted = st;
shift_german_string_heaps(&mut shifted, 7);
let grown = [&[0u8; 7][..], &blob].concat();
assert_eq!(german_string_content(&shifted, &grown), s, "shifted by 7");
}
#[test]
fn roundtrip_across_length_boundaries() {
for prefix in [0usize, 5] {
roundtrip(b"", prefix); roundtrip(b"a", prefix); roundtrip(b"abcd", prefix); roundtrip(b"abcde", prefix); roundtrip(b"\xc3\xa9", prefix); roundtrip(b"abcdefghijkl", prefix); roundtrip(b"abcdefghijklm", prefix); roundtrip(b"abcdefghijklmnopqrstuvwxyz", prefix); roundtrip(&vec![0xABu8; 1000], prefix); }
}
#[test]
fn blob_extent_truth_table() {
assert_eq!(blob_extent(8, 2, 3), Some(2..5));
assert_eq!(blob_extent(8, 0, 8), Some(0..8));
assert_eq!(blob_extent(8, 8, 0), Some(8..8));
assert_eq!(blob_extent(8, 6, 10), None);
assert_eq!(blob_extent(8, 99, 1), None);
assert_eq!(blob_extent(8, 4, usize::MAX), None);
assert_eq!(blob_extent(8, u64::MAX - 5, 20), None);
}
#[test]
fn compare_corrupt_long_cell_degrades_to_empty_without_panic() {
let mut a = encode_german_string(&[b'a'; 20], &mut Vec::new());
write_u64_le(&mut a, 8, 500);
let mut b = a;
write_u64_le(&mut b, 8, 900);
let blob = vec![0u8; 4];
assert_eq!(german_string_content(&a, &blob), &[] as &[u8]);
assert_eq!(compare_german_strings(&a, &blob, &b, &blob), Ordering::Equal);
assert!(!german_string_cell_ok(&a, &blob));
assert!(!german_string_cell_ok(&b, &blob));
}
#[test]
fn cell_ok_accepts_canonical_and_rejects_compare_visible_corruption() {
let mut blob = vec![0x7Fu8; 3];
let clean = encode_german_string(b"ab", &mut blob);
let mut dirty = clean;
dirty[6..8].copy_from_slice(&[0xFF, 0xFF]);
assert_eq!(
german_string_content(&clean, &blob),
german_string_content(&dirty, &blob)
);
assert_ne!(
compare_german_strings(&clean, &blob, &dirty, &blob),
Ordering::Equal,
"the pad is compare-visible — that is exactly what cell_ok must reject"
);
assert!(german_string_cell_ok(&clean, &blob));
assert!(!german_string_cell_ok(&dirty, &blob));
let mut dirty_suffix = encode_german_string(b"abcde", &mut blob);
dirty_suffix[15] = 0x01;
assert!(!german_string_cell_ok(&dirty_suffix, &blob));
let mut skewed = encode_german_string(b"abcdefghijklmnopqrstuvwxyz", &mut blob);
assert!(german_string_cell_ok(&skewed, &blob));
skewed[4] = b'z';
assert!(!german_string_cell_ok(&skewed, &blob));
}
#[test]
fn compare_matches_byte_order_across_classes_and_arenas() {
let data: &[&[u8]] = &[
b"",
b"a",
b"ab",
b"ab\0", b"abc",
b"abcd",
b"abcde",
b"abce", b"abcdefghijkl", b"abcdefghijklm", b"abcdefghijklmnopqrst", b"abcdefghijklmnopqrsu", b"abcdefghijklmnopqrst", b"abcd\0fghijklmnopqrst", b"zyxwvutsrqponmlkjihgfedcba", ];
let arena = |prefix: usize| {
let mut blob = vec![0x7Fu8; prefix];
let cells: Vec<[u8; 16]> = data.iter().map(|d| encode_german_string(d, &mut blob)).collect();
(cells, blob)
};
let (a, b) = (arena(3), arena(41));
for (x, y) in [(&a, &a), (&a, &b), (&b, &a)] {
for (i, di) in data.iter().enumerate() {
for (j, dj) in data.iter().enumerate() {
assert_eq!(
compare_german_strings(&x.0[i], &x.1, &y.0[j], &y.1),
di.cmp(dj),
"compare mismatch for {di:?} vs {dj:?}",
);
}
}
}
}