Refactor tests and move them to a dedicated module

- Moved the test suite from `lib.rs` to a new `tests.rs` file for better organization.
- Added a `SyntheticRsliEntry` struct to facilitate synthetic test cases.
- Introduced `RsliBuildOptions` struct to manage options for building RsLi byte arrays.
- Implemented various utility functions for file handling, data compression, and bit manipulation.
- Enhanced the `rsli_read_unpack_and_repack_all_files` test to validate all RsLi archives.
- Added new tests for synthetic entries covering all packing methods, overlay handling, and validation error cases.
This commit is contained in:
2026-02-09 23:11:11 +00:00
parent e08b5f3853
commit 371a060eb6
4 changed files with 1397 additions and 428 deletions

View File

@@ -27,6 +27,7 @@ impl Default for OpenOptions {
}
}
#[derive(Debug)]
pub struct Library {
bytes: Arc<[u8]>,
entries: Vec<EntryRecord>,
@@ -979,187 +980,4 @@ fn needs_xor_key(method: PackMethod) -> bool {
}
#[cfg(test)]
mod tests {
use super::*;
use std::any::Any;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::path::PathBuf;
fn collect_files_recursive(root: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = fs::read_dir(root) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_files_recursive(&path, out);
} else if path.is_file() {
out.push(path);
}
}
}
fn rsli_test_files() -> Vec<PathBuf> {
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("..")
.join("testdata")
.join("rsli");
let mut files = Vec::new();
collect_files_recursive(&root, &mut files);
files.sort();
files
.into_iter()
.filter(|path| {
fs::read(path)
.map(|data| data.get(0..4) == Some(b"NL\0\x01"))
.unwrap_or(false)
})
.collect()
}
fn panic_message(payload: Box<dyn Any + Send>) -> String {
let any = payload.as_ref();
if let Some(message) = any.downcast_ref::<String>() {
return message.clone();
}
if let Some(message) = any.downcast_ref::<&str>() {
return (*message).to_string();
}
String::from("panic without message")
}
#[test]
fn rsli_read_unpack_and_repack_all_files() {
let files = rsli_test_files();
assert!(!files.is_empty(), "testdata/rsli contains no RsLi archives");
let checked = files.len();
let mut success = 0usize;
let mut failures = Vec::new();
for path in files {
let display_path = path.display().to_string();
let result = catch_unwind(AssertUnwindSafe(|| {
let original = fs::read(&path).expect("failed to read archive");
let library = Library::open_path(&path)
.unwrap_or_else(|err| panic!("failed to open {}: {err}", path.display()));
let count = library.entry_count();
assert_eq!(
count,
library.entries().count(),
"entry count mismatch: {}",
path.display()
);
for idx in 0..count {
let id = EntryId(idx as u32);
let meta_ref = library
.get(id)
.unwrap_or_else(|| panic!("missing entry #{idx} in {}", path.display()));
let loaded = library.load(id).unwrap_or_else(|err| {
panic!("load failed for {} entry #{idx}: {err}", path.display())
});
let packed = library.load_packed(id).unwrap_or_else(|err| {
panic!(
"load_packed failed for {} entry #{idx}: {err}",
path.display()
)
});
let unpacked = library.unpack(&packed).unwrap_or_else(|err| {
panic!("unpack failed for {} entry #{idx}: {err}", path.display())
});
assert_eq!(
loaded,
unpacked,
"load != unpack in {} entry #{idx}",
path.display()
);
let mut out = Vec::new();
let written = library.load_into(id, &mut out).unwrap_or_else(|err| {
panic!(
"load_into failed for {} entry #{idx}: {err}",
path.display()
)
});
assert_eq!(
written,
loaded.len(),
"load_into size mismatch in {} entry #{idx}",
path.display()
);
assert_eq!(
out,
loaded,
"load_into payload mismatch in {} entry #{idx}",
path.display()
);
let fast = library.load_fast(id).unwrap_or_else(|err| {
panic!(
"load_fast failed for {} entry #{idx}: {err}",
path.display()
)
});
assert_eq!(
fast.as_slice(),
loaded.as_slice(),
"load_fast mismatch in {} entry #{idx}",
path.display()
);
let found = library.find(&meta_ref.meta.name).unwrap_or_else(|| {
panic!(
"find failed for '{}' in {}",
meta_ref.meta.name,
path.display()
)
});
let found_meta = library.get(found).expect("find returned invalid entry id");
assert_eq!(
found_meta.meta.name,
meta_ref.meta.name,
"find returned a different entry in {}",
path.display()
);
}
let rebuilt = library
.rebuild_from_parsed_metadata()
.unwrap_or_else(|err| panic!("rebuild failed for {}: {err}", path.display()));
assert_eq!(
rebuilt,
original,
"byte-to-byte roundtrip mismatch for {}",
path.display()
);
}));
match result {
Ok(()) => success += 1,
Err(payload) => {
failures.push(format!("{}: {}", display_path, panic_message(payload)));
}
}
}
let failed = failures.len();
eprintln!(
"RsLi summary: checked={}, success={}, failed={}",
checked, success, failed
);
if !failures.is_empty() {
panic!(
"RsLi validation failed.\nsummary: checked={}, success={}, failed={}\n{}",
checked,
success,
failed,
failures.join("\n")
);
}
}
}
mod tests;