use casc_storage::archive::ArchiveReader;
use casc_storage::error::Result;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_large_archive_memory_mapping_limits() {
let small_archive = create_test_archive(1024).unwrap(); let reader = ArchiveReader::open(small_archive.path()).unwrap();
assert!(
reader.is_memory_mapped(),
"Small files should be memory-mapped"
);
#[cfg(target_pointer_width = "32")]
{
use casc_storage::archive::ArchiveReader;
assert!(
ArchiveReader::can_memory_map(1024 * 1024 * 1024),
"1GB should be mappable on 32-bit"
);
assert!(
!ArchiveReader::can_memory_map(3 * 1024 * 1024 * 1024),
"3GB should not be mappable on 32-bit"
);
}
#[cfg(target_pointer_width = "64")]
{
use casc_storage::archive::ArchiveReader;
assert!(
ArchiveReader::can_memory_map(8 * 1024 * 1024 * 1024),
"8GB should be mappable on 64-bit"
);
assert!(
ArchiveReader::can_memory_map(64 * 1024 * 1024 * 1024),
"64GB should be mappable on 64-bit"
);
let too_large = 200u64 * 1024 * 1024 * 1024; assert!(
!ArchiveReader::can_memory_map(too_large),
"200GB should not be mappable to avoid VM exhaustion"
);
}
}
#[test]
fn test_fallback_file_reading() {
let test_data = b"This is test data for large archive fallback testing. It should be readable even without memory mapping.";
let archive = create_test_archive_with_data(test_data).unwrap();
let reader = ArchiveReader::open(archive.path()).unwrap();
let mut reader = reader;
let data = reader.read_at(0, 10).unwrap();
assert_eq!(&data, b"This is te");
let data = reader.read_at(50, 20).unwrap();
assert_eq!(&data, b"ng. It should be rea");
let end_offset = test_data.len() - 10;
let data = reader.read_at(end_offset as u64, 10).unwrap();
assert_eq!(&data, b"y mapping.");
}
#[test]
fn test_positioned_reads_thread_safety() {
use std::sync::Arc;
use std::thread;
let mut test_data = Vec::new();
for i in 0..1000u32 {
test_data.extend_from_slice(&i.to_le_bytes());
}
let archive = create_test_archive_with_data(&test_data).unwrap();
let reader = Arc::new(ArchiveReader::open(archive.path()).unwrap());
let mut handles = vec![];
for thread_id in 0..10 {
let reader_clone = Arc::clone(&reader);
let handle = thread::spawn(move || {
let offset = (thread_id * 40) as u64;
let data = reader_clone.read_at_cow(offset, 40).unwrap();
for (i, chunk) in data.chunks(4).enumerate() {
let expected_value = (thread_id * 10 + i) as u32;
let actual_value = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
assert_eq!(
actual_value, expected_value,
"Thread {thread_id} failed at position {i}"
);
}
thread_id
});
handles.push(handle);
}
let mut completed_threads = Vec::new();
for handle in handles {
completed_threads.push(handle.join().unwrap());
}
completed_threads.sort();
assert_eq!(completed_threads, (0..10).collect::<Vec<_>>());
}
#[test]
fn test_archive_section_streaming() {
let test_data = b"Streaming test data for archive sections. This should work for both memory-mapped and file-based access.";
let archive = create_test_archive_with_data(test_data).unwrap();
let reader = ArchiveReader::open(archive.path()).unwrap();
let section = reader.reader_at(20, 30).unwrap();
let mut section = section;
use std::io::Read;
let mut buffer = [0u8; 15];
let bytes_read = section.read(&mut buffer).unwrap();
assert_eq!(bytes_read, 15);
assert_eq!(&buffer, b"for archive sec");
let mut buffer2 = [0u8; 15];
let bytes_read2 = section.read(&mut buffer2).unwrap();
assert_eq!(bytes_read2, 15);
assert_eq!(&buffer2, b"tions. This sho");
}
#[test]
fn test_memory_mapping_vs_file_reading_performance() {
let test_data = vec![0u8; 1024 * 1024]; let mmap_archive = create_test_archive_with_data(&test_data).unwrap();
let reader = ArchiveReader::open(mmap_archive.path()).unwrap();
assert!(
reader.is_memory_mapped(),
"Should use memory mapping for 1MB file"
);
let start = std::time::Instant::now();
for i in 0..100 {
let offset = (i * 1000) as u64;
let _data = reader.read_slice(offset, 100).unwrap();
}
let mmap_time = start.elapsed();
println!("Memory-mapped reads: {mmap_time:?}");
}
#[test]
fn test_bounds_checking() {
let test_data = b"Small test data";
let archive = create_test_archive_with_data(test_data).unwrap();
let mut reader = ArchiveReader::open(archive.path()).unwrap();
let result = reader.read_at(100, 10);
assert!(
result.is_err(),
"Should fail when reading beyond file bounds"
);
let file_size = test_data.len() as u64;
let result = reader.read_at(file_size - 5, 10);
assert!(result.is_err(), "Should fail when read extends beyond file");
let result = reader.read_at(file_size - 5, 5);
assert!(result.is_ok(), "Should succeed for valid read at end");
}
#[test]
fn test_prefetch_optimization() {
let test_data = vec![0u8; 100 * 1024]; let archive = create_test_archive_with_data(&test_data).unwrap();
let reader = ArchiveReader::open(archive.path()).unwrap();
let result = reader.prefetch(0, 4096);
assert!(result.is_ok(), "Prefetch should succeed");
let result = reader.prefetch(50 * 1024, 100 * 1024);
assert!(result.is_ok(), "Prefetch should handle bounds gracefully");
}
fn create_test_archive(size: usize) -> Result<NamedTempFile> {
let mut file = NamedTempFile::new()?;
let data = vec![0u8; size];
file.write_all(&data)?;
file.flush()?;
Ok(file)
}
fn create_test_archive_with_data(data: &[u8]) -> Result<NamedTempFile> {
let mut file = NamedTempFile::new()?;
file.write_all(data)?;
file.flush()?;
Ok(file)
}
#[test]
fn test_large_archive_simulation() {
let mut archive_data = Vec::new();
struct SimulatedFile {
offset: u64,
size: u32,
content: Vec<u8>,
}
let mut simulated_files = Vec::new();
let mut current_offset = 0u64;
for i in 0..100 {
let size = 1024 + (i * 47) % 8192; let content: Vec<u8> = (0..size).map(|j| ((i + j) % 256) as u8).collect();
simulated_files.push(SimulatedFile {
offset: current_offset,
size: size as u32,
content: content.clone(),
});
archive_data.extend_from_slice(&content);
current_offset += size as u64;
}
let archive = create_test_archive_with_data(&archive_data).unwrap();
let mut reader = ArchiveReader::open(archive.path()).unwrap();
for (i, sim_file) in simulated_files.iter().enumerate() {
let read_data = reader
.read_at(sim_file.offset, sim_file.size as usize)
.unwrap();
assert_eq!(
read_data, sim_file.content,
"Simulated file {i} content mismatch"
);
}
println!(
"Successfully tested {} simulated files in archive",
simulated_files.len()
);
}