aboutsummaryrefslogtreecommitdiff
path: root/crates/rsli/src/tests.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-02-12 00:43:40 +0300
committerValentin Popov <valentin@popov.link>2026-02-12 00:43:40 +0300
commit662b292b5b47d0f7df3b19808db746bbc2ecc48c (patch)
treedf80622739c975eda645acf8754f3331cbdd1c82 /crates/rsli/src/tests.rs
parent3410b54793c3a1808e58d0fae94fb2ebd5f81015 (diff)
downloadfparkan-662b292b5b47d0f7df3b19808db746bbc2ecc48c.tar.xz
fparkan-662b292b5b47d0f7df3b19808db746bbc2ecc48c.zip
feat: обновить методы обработки данных и улучшить обработку ошибок в библиотеке
Diffstat (limited to 'crates/rsli/src/tests.rs')
-rw-r--r--crates/rsli/src/tests.rs174
1 files changed, 174 insertions, 0 deletions
diff --git a/crates/rsli/src/tests.rs b/crates/rsli/src/tests.rs
index ade5c2c..33fc71b 100644
--- a/crates/rsli/src/tests.rs
+++ b/crates/rsli/src/tests.rs
@@ -2,6 +2,7 @@ use super::*;
use crate::compress::lzh::{LZH_MAX_FREQ, LZH_N_CHAR, LZH_R, LZH_T};
use crate::compress::xor::xor_stream;
use flate2::write::DeflateEncoder;
+use flate2::write::ZlibEncoder;
use flate2::Compression;
use std::any::Any;
use std::fs;
@@ -103,6 +104,12 @@ fn deflate_raw(data: &[u8]) -> Vec<u8> {
encoder.finish().expect("deflate encoder finish failed")
}
+fn deflate_zlib(data: &[u8]) -> Vec<u8> {
+ let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
+ encoder.write_all(data).expect("zlib encoder write failed");
+ encoder.finish().expect("zlib encoder finish failed")
+}
+
fn lzss_pack_literals(data: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
for chunk in data.chunks(8) {
@@ -796,6 +803,131 @@ fn rsli_synthetic_all_methods_roundtrip() {
}
#[test]
+fn rsli_find_falls_back_when_sort_table_corrupted_in_memory() {
+ let entries = vec![
+ SyntheticRsliEntry {
+ name: "AAA".to_string(),
+ method_raw: 0x000,
+ plain: b"a".to_vec(),
+ declared_packed_size: None,
+ },
+ SyntheticRsliEntry {
+ name: "BBB".to_string(),
+ method_raw: 0x000,
+ plain: b"b".to_vec(),
+ declared_packed_size: None,
+ },
+ SyntheticRsliEntry {
+ name: "CCC".to_string(),
+ method_raw: 0x000,
+ plain: b"c".to_vec(),
+ declared_packed_size: None,
+ },
+ ];
+ let bytes = build_rsli_bytes(
+ &entries,
+ &RsliBuildOptions {
+ presorted: true,
+ ..RsliBuildOptions::default()
+ },
+ );
+ let path = write_temp_file("rsli-find-fallback", &bytes);
+
+ let mut library = Library::open_path(&path).expect("open synthetic rsli failed");
+ library.entries[1].sort_to_original = -1;
+
+ assert_eq!(library.find("AAA"), Some(EntryId(0)));
+ assert_eq!(library.find("bbb"), Some(EntryId(1)));
+ assert_eq!(library.find("CcC"), Some(EntryId(2)));
+ assert_eq!(library.find("missing"), None);
+
+ let _ = fs::remove_file(&path);
+}
+
+#[test]
+fn rsli_deflate_method_rejects_zlib_wrapped_stream() {
+ let plain = b"payload".to_vec();
+ let zlib_payload = deflate_zlib(&plain);
+ let entries = vec![SyntheticRsliEntry {
+ name: "ZLIB".to_string(),
+ method_raw: 0x100,
+ plain,
+ declared_packed_size: Some(
+ u32::try_from(zlib_payload.len()).expect("zlib payload size overflow"),
+ ),
+ }];
+ let mut bytes = build_rsli_bytes(
+ &entries,
+ &RsliBuildOptions {
+ presorted: true,
+ ..RsliBuildOptions::default()
+ },
+ );
+
+ let table_end = 32 + entries.len() * 32;
+ let data_offset = table_end;
+ let data_end = data_offset + zlib_payload.len();
+ if bytes.len() < data_end {
+ bytes.resize(data_end, 0);
+ }
+ bytes[data_offset..data_end].copy_from_slice(&zlib_payload);
+
+ let path = write_temp_file("rsli-zlib-reject", &bytes);
+ let library = Library::open_path(&path).expect("open zlib-wrapped rsli failed");
+ match library.load(EntryId(0)) {
+ Err(Error::DecompressionFailed(reason)) => {
+ assert_eq!(reason, "deflate");
+ }
+ other => panic!("expected deflate decompression error, got {other:?}"),
+ }
+ let _ = fs::remove_file(&path);
+}
+
+#[test]
+fn rsli_lzss_huffman_reports_unexpected_eof() {
+ let entries = vec![SyntheticRsliEntry {
+ name: "TRUNC".to_string(),
+ method_raw: 0x080,
+ plain: b"this payload is long enough".to_vec(),
+ declared_packed_size: None,
+ }];
+ let mut bytes = build_rsli_bytes(
+ &entries,
+ &RsliBuildOptions {
+ presorted: true,
+ ..RsliBuildOptions::default()
+ },
+ );
+
+ let seed = read_u32_le(&bytes, 20);
+ let mut table_plain = xor_stream(&bytes[32..64], (seed & 0xFFFF) as u16);
+ let original_packed_size = u32::from_le_bytes([
+ table_plain[28],
+ table_plain[29],
+ table_plain[30],
+ table_plain[31],
+ ]);
+ assert!(
+ original_packed_size > 4,
+ "packed payload too small for truncation"
+ );
+ let truncated_size = original_packed_size - 3;
+ table_plain[28..32].copy_from_slice(&truncated_size.to_le_bytes());
+ let encrypted_table = xor_stream(&table_plain, (seed & 0xFFFF) as u16);
+ bytes[32..64].copy_from_slice(&encrypted_table);
+
+ let path = write_temp_file("rsli-lzh-truncated", &bytes);
+ let library = Library::open_path(&path).expect("open truncated lzh rsli failed");
+ match library.load(EntryId(0)) {
+ Err(Error::DecompressionFailed(reason)) => {
+ assert_eq!(reason, "lzss-huffman: unexpected EOF");
+ }
+ other => panic!("expected lzss-huffman EOF error, got {other:?}"),
+ }
+ let _ = fs::remove_file(&path);
+}
+
+#[test]
fn rsli_presorted_flag_requires_permutation() {
let entries = vec![
SyntheticRsliEntry {
@@ -843,6 +975,48 @@ fn rsli_presorted_flag_requires_permutation() {
}
#[test]
+fn rsli_load_reports_correct_entry_id_on_range_failure() {
+ let entries = vec![
+ SyntheticRsliEntry {
+ name: "ONE".to_string(),
+ method_raw: 0x000,
+ plain: b"one".to_vec(),
+ declared_packed_size: None,
+ },
+ SyntheticRsliEntry {
+ name: "TWO".to_string(),
+ method_raw: 0x000,
+ plain: b"two".to_vec(),
+ declared_packed_size: None,
+ },
+ ];
+ let bytes = build_rsli_bytes(
+ &entries,
+ &RsliBuildOptions {
+ presorted: true,
+ ..RsliBuildOptions::default()
+ },
+ );
+ let path = write_temp_file("rsli-entry-id-error", &bytes);
+
+ let mut library = Library::open_path(&path).expect("open synthetic rsli failed");
+ library.entries[1].packed_size_available = usize::MAX;
+
+ match library.load(EntryId(1)) {
+ Err(Error::IntegerOverflow) => {}
+ other => panic!("expected IntegerOverflow, got {other:?}"),
+ }
+
+ library.entries[1].packed_size_available = library.bytes.len();
+ match library.load(EntryId(1)) {
+ Err(Error::EntryDataOutOfBounds { id, .. }) => assert_eq!(id, 1),
+ other => panic!("expected EntryDataOutOfBounds with id=1, got {other:?}"),
+ }
+
+ let _ = fs::remove_file(&path);
+}
+
+#[test]
fn rsli_xorlzss_huffman_on_the_fly_roundtrip() {
let plain: Vec<u8> = (0..512u16).map(|i| b'A' + (i % 26) as u8).collect();
let entries = vec![SyntheticRsliEntry {