use packdir::packer::{Gzip, Packer, SIG, VERSION, Xz};
use packdir::unpack::{
PackedData, PackedDir, PackedFileRecord, read_le, try_read_le, try_read_utf8_string,
};
use packdir::*;
use std::collections::HashMap;
fn create_mock_packed_data(files: &[(&str, &[u8], Packer)]) -> Vec<u8> {
let mut header = Vec::new();
let mut data = Vec::new();
let mut records = Vec::new();
header.extend_from_slice(SIG);
header.push(VERSION);
header.extend_from_slice(&(files.len() as u32).to_le_bytes());
let mut data_offset = 0u64;
for (path, content, packer) in files {
let packed_content = packer.pack(content).unwrap();
let unpacked_size = content.len() as u64;
let packed_size = packed_content.len() as u64;
records.push((path, unpacked_size, packed_size, data_offset, *packer));
data.extend_from_slice(&packed_content);
data_offset += packed_size;
}
let mut header_size = 8 + 1 + 4; for (path, _, _, _, _) in &records {
header_size += 2 + path.len() + 8 + 8 + 8 + 1; }
for (path, unpacked_size, packed_size, data_offset, packer) in records {
let path_bytes = path.as_bytes();
header.extend_from_slice(&(path_bytes.len() as u16).to_le_bytes());
header.extend_from_slice(path_bytes);
header.extend_from_slice(&unpacked_size.to_le_bytes());
header.extend_from_slice(&packed_size.to_le_bytes());
header.extend_from_slice(&(header_size as u64 + data_offset).to_le_bytes());
header.push(packer.into());
}
header.extend_from_slice(&data);
header
}
#[test]
fn test_packed_data_from_static() {
let static_data: &'static [u8] = b"test data";
let packed = PackedData::from_static(static_data);
assert_eq!(packed.as_bytes(), static_data);
assert_eq!(packed.len(), 9);
assert!(!packed.is_empty());
}
#[test]
fn test_packed_data_from_owned() {
let owned_data = vec![1, 2, 3, 4, 5];
let packed = PackedData::from_owned(owned_data.clone());
assert_eq!(packed.as_bytes(), owned_data);
assert_eq!(packed.len(), 5);
assert!(!packed.is_empty());
}
#[test]
fn test_packed_data_into_owned() {
let static_data: &'static [u8] = b"test data";
let packed = PackedData::from_static(static_data);
let owned = packed.into_owned();
assert_eq!(owned.as_bytes(), static_data);
let owned_data = vec![1, 2, 3];
let packed_owned = PackedData::from_owned(owned_data.clone());
let still_owned = packed_owned.into_owned();
assert_eq!(still_owned.as_bytes(), owned_data);
}
#[test]
fn test_packed_data_empty() {
let empty_static = PackedData::from_static(b"");
assert!(empty_static.is_empty());
assert_eq!(empty_static.len(), 0);
let empty_owned = PackedData::from_owned(vec![]);
assert!(empty_owned.is_empty());
assert_eq!(empty_owned.len(), 0);
}
#[test]
fn test_packed_data_from_conversions() {
let static_data: &'static [u8] = b"static";
let packed_from_static: PackedData = static_data.into();
assert_eq!(packed_from_static.as_bytes(), static_data);
let owned_data = vec![1, 2, 3];
let packed_from_owned: PackedData = owned_data.clone().into();
assert_eq!(packed_from_owned.as_bytes(), owned_data);
}
#[test]
fn test_packed_file_record_try_read() {
let mut data = Vec::new();
data.extend_from_slice(&100u64.to_le_bytes()); data.extend_from_slice(&80u64.to_le_bytes()); data.extend_from_slice(&1024u64.to_le_bytes()); data.push(1u8);
let mut offset = 0;
let record = PackedFileRecord::try_read(&data, &mut offset).unwrap();
assert_eq!(record.unpacked_size, 100);
assert_eq!(record.packed_size, 80);
assert_eq!(record.data_start, 1024);
assert_eq!(record.packer, Packer::Gzip(Gzip::default()));
assert_eq!(offset, data.len());
}
#[test]
fn test_packed_file_record_try_read_invalid() {
let data = vec![1, 2, 3]; let mut offset = 0;
assert!(PackedFileRecord::try_read(&data, &mut offset).is_err());
}
#[test]
fn test_packed_dir_invalid_signature() {
let invalid_data = b"INVALID_SIG";
let result = PackedDir::new(invalid_data);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Invalid archive"));
}
#[test]
fn test_packed_dir_invalid_version() {
let mut data = Vec::new();
data.extend_from_slice(SIG);
data.push(99u8);
let result = PackedDir::from_data(PackedData::from_owned(data));
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Unsupported archive version")
);
}
#[test]
fn test_packed_dir_empty_archive() {
let packed_data = create_mock_packed_data(&[]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert_eq!(archive.entries().len(), 0);
assert_eq!(archive.records_len(), 0);
assert_eq!(archive.cache_len(), 0);
assert!(archive.is_fully_cached());
assert!(archive.is_packed_data_freed());
}
#[test]
fn test_packed_dir_single_file() {
let content = b"Hello, World!";
let packed_data = create_mock_packed_data(&[("hello.txt", content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert_eq!(archive.entries().len(), 1);
assert!(archive.has_entry("hello.txt"));
assert!(!archive.has_entry("nonexistent.txt"));
let record = archive.record("hello.txt").unwrap();
assert_eq!(record.unpacked_size, content.len() as u64);
assert_eq!(record.packer, Packer::Raw);
}
#[test]
fn test_packed_dir_multiple_files_different_compression() {
let files = vec![
("file1.txt", b"Content 1".as_slice(), Packer::Raw),
(
"file2.txt",
b"Content 2 with more text".as_slice(),
Packer::Gzip(Gzip::Level6),
),
(
"file3.txt",
b"Content 3 with even more text for compression".as_slice(),
Packer::Xz(Xz::Level1),
),
];
let packed_data = create_mock_packed_data(&files);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert_eq!(archive.entries().len(), 3);
assert_eq!(archive.records_len(), 3);
for (filename, content, packer) in &files {
assert!(archive.has_entry(filename));
let record = archive.record(filename).unwrap();
assert_eq!(record.unpacked_size, content.len() as u64);
assert_eq!(
std::mem::discriminant(&record.packer),
std::mem::discriminant(packer)
);
}
}
#[test]
fn test_packed_dir_get_cached() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert!(archive.get_cached("test.txt").is_none());
assert!(archive.get_cached("nonexistent.txt").is_none());
}
#[test]
fn test_packed_dir_get_mutable() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let retrieved = archive.get("test.txt").unwrap();
assert_eq!(retrieved.as_ref(), content);
assert!(archive.get_cached("test.txt").is_some());
assert_eq!(archive.cache_len(), 1);
let retrieved_again = archive.get("test.txt").unwrap();
assert_eq!(retrieved_again.as_ref(), content);
}
#[test]
fn test_packed_dir_get_always() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let retrieved = archive.get_always("test.txt").unwrap();
assert_eq!(retrieved.as_ref(), content);
assert!(archive.get_cached("test.txt").is_none());
assert_eq!(archive.cache_len(), 0);
}
#[test]
fn test_packed_dir_get_nonexistent() {
let packed_data = create_mock_packed_data(&[("test.txt", b"content", Packer::Raw)]);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let result = archive.get("nonexistent.txt");
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("File not found"));
}
#[test]
fn test_packed_dir_inflate() {
let files = vec![
("file1.txt", b"Content 1".as_slice(), Packer::Raw),
(
"file2.txt",
b"Content 2".as_slice(),
Packer::Gzip(Gzip::Level6),
),
];
let packed_data = create_mock_packed_data(&files);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert_eq!(archive.cache_len(), 0);
assert!(!archive.is_fully_cached());
assert!(!archive.is_packed_data_freed());
let inflated = archive.inflate().unwrap();
assert_eq!(inflated.cache_len(), 2);
assert!(inflated.is_fully_cached());
assert!(inflated.is_packed_data_freed());
for (filename, content, _) in &files {
let cached = inflated.get_cached(filename).unwrap();
assert_eq!(cached.as_ref(), *content);
}
}
#[test]
fn test_packed_dir_inflated() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let inflated = archive.inflated().unwrap();
assert!(inflated.is_fully_cached());
assert!(inflated.is_packed_data_freed());
assert!(!archive.is_fully_cached());
assert!(!archive.is_packed_data_freed());
}
#[test]
fn test_packed_dir_partial_cache_and_free() {
let files = vec![
("file1.txt", b"Content 1".as_slice(), Packer::Raw),
("file2.txt", b"Content 2".as_slice(), Packer::Raw),
];
let packed_data = create_mock_packed_data(&files);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
archive.get("file1.txt").unwrap();
assert_eq!(archive.cache_len(), 1);
assert!(!archive.is_fully_cached());
assert!(!archive.is_packed_data_freed());
archive.get("file2.txt").unwrap();
assert_eq!(archive.cache_len(), 2);
assert!(archive.is_fully_cached());
assert!(archive.is_packed_data_freed());
}
#[test]
fn test_packed_dir_get_after_data_freed() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let mut inflated = archive.inflate().unwrap();
assert!(inflated.is_packed_data_freed());
let cached = inflated.get("test.txt").unwrap();
assert_eq!(cached.as_ref(), content);
}
#[test]
fn test_packed_dir_get_always_after_data_freed() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let inflated = archive.inflate().unwrap();
assert!(inflated.is_packed_data_freed());
let cached = inflated.get_always("test.txt").unwrap();
assert_eq!(cached.as_ref(), content);
}
#[test]
fn test_packed_dir_get_always_non_cached_after_freed() {
let files = vec![
("cached.txt", b"Cached content".as_slice(), Packer::Raw),
(
"not_cached.txt",
b"Not cached content".as_slice(),
Packer::Raw,
),
];
let packed_data = create_mock_packed_data(&files);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
archive.get("cached.txt").unwrap();
let test_archive = PackedDir {
records: archive.records.clone(),
packed_data: None, unpack_cache: {
let mut cache = HashMap::new();
cache.insert("cached.txt".to_string(), b"Cached content".to_vec());
cache
},
};
let cached = test_archive.get_always("cached.txt").unwrap();
assert_eq!(cached.as_ref(), b"Cached content");
let result = test_archive.get_always("not_cached.txt");
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Packed data has been freed")
);
}
#[test]
fn test_packed_dir_compression_round_trip() {
let original_content = b"This is test content for compression testing. ".repeat(5); let files = vec![
("raw.txt", original_content.as_slice(), Packer::Raw),
(
"gzip.txt",
original_content.as_slice(),
Packer::Gzip(Gzip::Level6),
),
(
"xz.txt",
original_content.as_slice(),
Packer::Xz(Xz::Level6),
),
];
let packed_data = create_mock_packed_data(&files);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
for (filename, expected_content, _) in &files {
let retrieved = archive.get(filename).unwrap();
assert_eq!(retrieved.as_ref(), *expected_content);
}
}
#[test]
fn test_packed_dir_large_content() {
let large_content = vec![42u8; 10_000]; let packed_data =
create_mock_packed_data(&[("large.bin", &large_content, Packer::Xz(Xz::Level1))]);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let retrieved = archive.get("large.bin").unwrap();
assert_eq!(retrieved.as_ref(), large_content);
}
#[test]
fn test_packed_dir_empty_file() {
let empty_content = b"";
let packed_data = create_mock_packed_data(&[("empty.txt", empty_content, Packer::Raw)]);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let retrieved = archive.get("empty.txt").unwrap();
assert_eq!(retrieved.as_ref(), empty_content);
assert!(retrieved.is_empty());
}
#[test]
fn test_packed_dir_utf8_paths() {
let content = b"Unicode test content";
let files = vec![
("normal.txt", content.as_slice(), Packer::Raw),
("café.txt", content.as_slice(), Packer::Raw),
("日本語.txt", content.as_slice(), Packer::Raw),
("folder/nested.txt", content.as_slice(), Packer::Raw),
];
let packed_data = create_mock_packed_data(&files);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
for (filename, expected_content, _) in &files {
assert!(archive.has_entry(filename));
let retrieved = archive.get(filename).unwrap();
assert_eq!(retrieved.as_ref(), *expected_content);
}
}
#[test]
fn test_packed_dir_entries_collection() {
let files = vec![
("z.txt", b"z".as_slice(), Packer::Raw),
("a.txt", b"a".as_slice(), Packer::Raw),
("m.txt", b"m".as_slice(), Packer::Raw),
];
let packed_data = create_mock_packed_data(&files);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let entries = archive.entries();
assert_eq!(entries.len(), 3);
assert!(entries.contains(&"z.txt"));
assert!(entries.contains(&"a.txt"));
assert!(entries.contains(&"m.txt"));
}
#[test]
fn test_read_le_helper() {
let data = vec![0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00];
let mut offset = 0;
let val16: u16 = read_le(&data, &mut offset).unwrap();
assert_eq!(val16, 0x00001234);
assert_eq!(offset, 2);
let val32: u32 = read_le(&data, &mut offset).unwrap();
assert_eq!(val32, 0x12345678);
assert_eq!(offset, 6);
let val16_zero: u16 = read_le(&data, &mut offset).unwrap();
assert_eq!(val16_zero, 0x00000000);
assert_eq!(offset, 8);
let val16_fail: Option<u16> = read_le(&data, &mut offset);
assert!(val16_fail.is_none());
}
#[test]
fn test_try_read_le_helper() {
let data = vec![0x34, 0x12, 0x78, 0x56];
let mut offset = 0;
let val16 = try_read_le::<u16>("test_u16", &data, &mut offset).unwrap();
assert_eq!(val16, 0x1234);
assert_eq!(offset, 2);
let val16_2 = try_read_le::<u16>("test_u16_2", &data, &mut offset).unwrap();
assert_eq!(val16_2, 0x5678);
assert_eq!(offset, 4);
let result = try_read_le::<u16>("test_fail", &data, &mut offset);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Failed to read test_fail")
);
}
#[test]
fn test_try_read_utf8_string_helper() {
let data = b"Hello, World!\xF0\x9F\x8C\x8D"; let mut offset = 0;
let hello = try_read_utf8_string("greeting", data, &mut offset, 13).unwrap();
assert_eq!(hello, "Hello, World!");
assert_eq!(offset, 13);
let emoji = try_read_utf8_string("emoji", data, &mut offset, 4).unwrap();
assert_eq!(emoji, "🌍");
assert_eq!(offset, 17);
let result = try_read_utf8_string("fail", data, &mut offset, 10);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Failed to read fail")
);
}
#[test]
fn test_try_read_utf8_string_invalid_utf8() {
let data = b"Valid\xFF\xFEInvalid";
let mut offset = 0;
let result = try_read_utf8_string("mixed", data, &mut offset, data.len()).unwrap();
assert!(result.contains("Valid"));
assert!(result.contains("Invalid"));
assert_eq!(offset, data.len());
}
#[test]
fn test_packed_dir_as_ref_implementations() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let archive_ref: &PackedDir = archive.as_ref();
assert!(archive_ref.has_entry("test.txt"));
fn create_archive() -> PackedDir {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap()
}
use std::sync::LazyLock;
let lazy_archive: LazyLock<PackedDir> = LazyLock::new(create_archive);
let lazy_ref: &PackedDir = lazy_archive.as_ref();
assert!(lazy_ref.has_entry("test.txt"));
}
#[test]
fn test_packed_dir_is_valid_packed_name() {
assert!(PackedDir::is_valid_packed_name("assets"));
assert!(PackedDir::is_valid_packed_name("my_bundle"));
assert!(PackedDir::is_valid_packed_name("bundle-name"));
assert!(PackedDir::is_valid_packed_name("bundle123"));
assert!(PackedDir::is_valid_packed_name("a"));
assert!(PackedDir::is_valid_packed_name("A"));
assert!(PackedDir::is_valid_packed_name("Bundle_Name-123"));
assert!(!PackedDir::is_valid_packed_name(""));
assert!(!PackedDir::is_valid_packed_name("123bundle"));
assert!(!PackedDir::is_valid_packed_name("bundle.name"));
assert!(!PackedDir::is_valid_packed_name("bundle name"));
assert!(!PackedDir::is_valid_packed_name("bundle@name"));
assert!(!PackedDir::is_valid_packed_name("bundle/name"));
assert!(!PackedDir::is_valid_packed_name("bundle\\name"));
}
#[test]
fn test_packed_dir_record_access() {
let files = vec![
("file1.txt", b"Content 1".as_slice(), Packer::Raw),
(
"file2.txt",
b"Content 2".as_slice(),
Packer::Gzip(Gzip::Level9),
),
("file3.txt", b"Content 3".as_slice(), Packer::Xz(Xz::Level1)),
];
let packed_data = create_mock_packed_data(&files);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
for (filename, content, packer) in &files {
let record = archive.record(filename).unwrap();
assert_eq!(record.unpacked_size, content.len() as u64);
assert_eq!(
std::mem::discriminant(&record.packer),
std::mem::discriminant(packer)
);
assert!(record.data_start > 0);
}
assert!(archive.record("nonexistent.txt").is_none());
}
#[test]
fn test_packed_dir_cow_behavior() {
let content = b"Test content";
let packed_data = create_mock_packed_data(&[("test.txt", content, Packer::Raw)]);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let borrowed = archive.get("test.txt").unwrap();
assert_eq!(borrowed.as_ref(), content);
matches!(borrowed, std::borrow::Cow::Borrowed(_));
let packed_data2 = create_mock_packed_data(&[("test2.txt", content, Packer::Raw)]);
let archive2 = PackedDir::from_data(PackedData::from_owned(packed_data2)).unwrap();
let owned = archive2.get_always("test2.txt").unwrap();
assert_eq!(owned.as_ref(), content);
matches!(owned, std::borrow::Cow::Owned(_));
}
#[test]
fn test_packed_dir_stress_many_files() {
let mut files = Vec::new();
for i in 0..50 {
let filename = format!("file_{i:04}.txt");
let content = format!("Content for file {i}");
files.push((filename, content.into_bytes(), Packer::Raw));
}
let files_ref: Vec<(&str, &[u8], Packer)> = files
.iter()
.map(|(name, content, packer)| (name.as_str(), content.as_slice(), *packer))
.collect();
let packed_data = create_mock_packed_data(&files_ref);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert_eq!(archive.records_len(), 50);
let test_file = archive.get_always("file_0025.txt").unwrap();
assert_eq!(test_file.as_ref(), b"Content for file 25");
for i in (0..50).step_by(10) {
let filename = format!("file_{:04}.txt", i);
assert!(archive.has_entry(&filename));
}
}
#[test]
fn test_packed_dir_compression_effectiveness() {
let repetitive_content = b"ABCD".repeat(50); let random_content: Vec<u8> = (0..200).map(|i| (i % 256) as u8).collect();
let files = vec![
(
"repetitive_raw.txt",
repetitive_content.as_slice(),
Packer::Raw,
),
(
"repetitive_gzip.txt",
repetitive_content.as_slice(),
Packer::Gzip(Gzip::Level9),
),
(
"repetitive_xz.txt",
repetitive_content.as_slice(),
Packer::Xz(Xz::Level9),
),
("random_raw.txt", random_content.as_slice(), Packer::Raw),
(
"random_gzip.txt",
random_content.as_slice(),
Packer::Gzip(Gzip::Level9),
),
(
"random_xz.txt",
random_content.as_slice(),
Packer::Xz(Xz::Level9),
),
];
let packed_data = create_mock_packed_data(&files);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
for (filename, expected_content, _) in &files {
let retrieved = archive.get_always(filename).unwrap();
assert_eq!(retrieved.as_ref(), *expected_content);
}
let raw_record = archive.record("repetitive_raw.txt").unwrap();
let gzip_record = archive.record("repetitive_gzip.txt").unwrap();
let xz_record = archive.record("repetitive_xz.txt").unwrap();
assert_eq!(raw_record.unpacked_size, gzip_record.unpacked_size);
assert_eq!(raw_record.unpacked_size, xz_record.unpacked_size);
assert!(gzip_record.packed_size < raw_record.packed_size);
assert!(xz_record.packed_size < raw_record.packed_size);
}
#[test]
fn test_invalid_archive_data() {
let short_data = b"PACK";
let result = PackedDir::new(short_data);
assert!(result.is_err());
let mut truncated = Vec::new();
truncated.extend_from_slice(SIG);
truncated.push(VERSION);
let result = PackedDir::from_data(PackedData::from_owned(truncated));
assert!(result.is_err());
let mut invalid_count = Vec::new();
invalid_count.extend_from_slice(SIG);
invalid_count.push(VERSION);
invalid_count.extend_from_slice(&u32::MAX.to_le_bytes()); let result = PackedDir::from_data(PackedData::from_owned(invalid_count));
assert!(result.is_err());
}
#[test]
fn test_mixed_compression_archive() {
let test_content = b"This is test content that will be compressed differently";
let files = vec![
("uncompressed.txt", test_content.as_slice(), Packer::Raw),
(
"gzip_fast.txt",
test_content.as_slice(),
Packer::Gzip(Gzip::Level1),
),
(
"gzip_best.txt",
test_content.as_slice(),
Packer::Gzip(Gzip::Level9),
),
(
"xz_fast.txt",
test_content.as_slice(),
Packer::Xz(Xz::Level1),
),
(
"xz_best.txt",
test_content.as_slice(),
Packer::Xz(Xz::Level9),
),
];
let packed_data = create_mock_packed_data(&files);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
for (filename, expected_content, _) in &files {
let retrieved = archive.get(filename).unwrap();
assert_eq!(retrieved.as_ref(), *expected_content);
let record = archive.record(filename).unwrap();
assert_eq!(record.unpacked_size, expected_content.len() as u64);
}
assert_eq!(archive.cache_len(), files.len());
assert!(archive.is_fully_cached());
assert!(archive.is_packed_data_freed());
}
#[test]
fn test_packed_dir_edge_cases() {
let long_path = "a".repeat(100); let content = b"content";
let packed_data = create_mock_packed_data(&[(&long_path, content, Packer::Raw)]);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert!(archive.has_entry(&long_path));
let retrieved = archive.get_always(&long_path).unwrap();
assert_eq!(retrieved.as_ref(), content);
let binary_content: Vec<u8> = (0..=255).collect();
let packed_data2 = create_mock_packed_data(&[("binary.bin", &binary_content, Packer::Raw)]);
let archive2 = PackedDir::from_data(PackedData::from_owned(packed_data2)).unwrap();
let retrieved_binary = archive2.get_always("binary.bin").unwrap();
assert_eq!(retrieved_binary.as_ref(), binary_content);
}
#[test]
fn test_packed_dir_file_path_edge_cases() {
let content = b"test content";
let files = vec![
("simple.txt", content.as_slice(), Packer::Raw),
("path/with/slashes.txt", content.as_slice(), Packer::Raw),
("file with spaces.txt", content.as_slice(), Packer::Raw),
("file-with-dashes.txt", content.as_slice(), Packer::Raw),
("file_with_underscores.txt", content.as_slice(), Packer::Raw),
("file.with.dots.txt", content.as_slice(), Packer::Raw),
("UPPERCASE.TXT", content.as_slice(), Packer::Raw),
("123numeric.txt", content.as_slice(), Packer::Raw),
];
let packed_data = create_mock_packed_data(&files);
let archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
for (filename, expected_content, _) in &files {
assert!(archive.has_entry(filename));
let retrieved = archive.get_always(filename).unwrap();
assert_eq!(retrieved.as_ref(), *expected_content);
}
}
#[test]
fn test_packed_dir_memory_efficiency() {
let files = vec![
("file1.txt", b"Content 1".as_slice(), Packer::Raw),
("file2.txt", b"Content 2".as_slice(), Packer::Raw),
("file3.txt", b"Content 3".as_slice(), Packer::Raw),
];
let packed_data = create_mock_packed_data(&files);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
assert!(!archive.is_packed_data_freed());
assert_eq!(archive.cache_len(), 0);
archive.get("file1.txt").unwrap();
assert!(!archive.is_packed_data_freed());
assert_eq!(archive.cache_len(), 1);
archive.get("file2.txt").unwrap();
assert!(!archive.is_packed_data_freed());
assert_eq!(archive.cache_len(), 2);
archive.get("file3.txt").unwrap();
assert!(archive.is_packed_data_freed());
assert_eq!(archive.cache_len(), 3);
assert!(archive.is_fully_cached());
}
#[test]
fn test_packed_dir_error_messages() {
let packed_data = create_mock_packed_data(&[("exists.txt", b"content", Packer::Raw)]);
let mut archive = PackedDir::from_data(PackedData::from_owned(packed_data)).unwrap();
let result = archive.get("missing.txt");
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("File not found"));
assert!(error_msg.contains("missing.txt"));
let invalid_data = b"WRONG SIG";
let result = PackedDir::new(invalid_data);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Invalid archive"));
let mut bad_version = Vec::new();
bad_version.extend_from_slice(SIG);
bad_version.push(255); let result = PackedDir::from_data(PackedData::from_owned(bad_version));
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Unsupported archive version")
);
}