use std::fs;
use std::panic;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use mdict_rs::{MdxFile, checksum::adler32, compression::decode_block};
#[test]
fn mutated_compression_blocks_do_not_panic() {
let block = zlib_block(b"hello mdict");
for index in 0..block.len() {
let mut mutated = block.clone();
mutated[index] ^= 0x5a;
let result = panic::catch_unwind(|| {
let _ = decode_block("mutated zlib block", &mutated, 11);
});
assert!(result.is_ok(), "decode_block panicked at byte {index}");
}
}
fn zlib_block(data: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&[2, 0, 0, 0]);
out.extend_from_slice(&adler32(data).to_be_bytes());
out.extend_from_slice(&miniz_oxide::deflate::compress_to_vec_zlib(data, 6));
out
}
#[test]
fn mutated_header_files_do_not_panic() {
let bytes = minimal_header_only_mdx();
for index in 0..bytes.len() {
let mut mutated = bytes.clone();
mutated[index] ^= 0xa5;
let path = write_temp_file("mutated-header", &mutated);
let result = panic::catch_unwind(|| {
let _ = MdxFile::open(&path);
});
fs::remove_file(path).ok();
assert!(result.is_ok(), "MdxFile::open panicked at byte {index}");
}
}
fn minimal_header_only_mdx() -> Vec<u8> {
let xml = "<Dictionary GeneratedByEngineVersion=\"2.0\" RequiredEngineVersion=\"2.0\" Encoding=\"UTF-8\"/>";
let utf16 = utf16le(xml);
let mut bytes = Vec::new();
bytes.extend_from_slice(&(utf16.len() as u32).to_be_bytes());
bytes.extend_from_slice(&utf16);
bytes.extend_from_slice(&adler32(&utf16).to_le_bytes());
bytes
}
fn utf16le(text: &str) -> Vec<u8> {
let mut out = Vec::new();
for unit in text.encode_utf16() {
out.extend_from_slice(&unit.to_le_bytes());
}
out
}
fn write_temp_file(prefix: &str, bytes: &[u8]) -> PathBuf {
let unique = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let path = std::env::temp_dir().join(format!("{prefix}-{unique}.mdx"));
fs::write(&path, bytes).unwrap();
path
}