use oxidize_pdf::document::Document;
use oxidize_pdf::error::Result;
use oxidize_pdf::memory::{MemoryManager, MemoryOptions};
use oxidize_pdf::page::Page;
use oxidize_pdf::parser::optimized_reader::OptimizedPdfReader;
use oxidize_pdf::parser::ParseOptions;
use oxidize_pdf::text::Font;
use std::fs;
use std::io::Cursor;
use tempfile::TempDir;
#[test]
fn test_memory_optimized_document_creation() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let memory_configs = vec![
("small_file", MemoryOptions::small_file()),
("large_file", MemoryOptions::large_file()),
(
"custom",
MemoryOptions::default()
.with_cache_size(500)
.with_lazy_loading(false),
),
];
for (config_name, _memory_options) in memory_configs {
let mut doc = Document::new();
doc.set_title(format!("Memory Test - {config_name}"));
for page_num in 1..=5 {
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 750.0)
.write(&format!("Memory config: {config_name} - Page {page_num}"))?;
let content = format!("Content for page {page_num} with config {config_name}");
for line in 0..20 {
let y_pos = 700.0 - (line as f64 * 15.0);
page.text()
.set_font(Font::Helvetica, 10.0)
.at(50.0, y_pos)
.write(&format!("{} - Line {}", content, line + 1))?;
}
doc.add_page(page);
}
let file_path = temp_dir.path().join(format!("memory_{config_name}.pdf"));
doc.save(&file_path)?;
assert!(file_path.exists());
let file_size = fs::metadata(&file_path).unwrap().len();
println!("Generated PDF size for {config_name}: {file_size} bytes");
assert!(file_size > 2000);
let pdf_bytes = doc.to_bytes()?;
assert!(!pdf_bytes.is_empty());
assert!(pdf_bytes.len() as u64 <= file_size + 1000); }
Ok(())
}
#[test]
fn test_optimized_reader_memory_workflows() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let mut test_doc = Document::new();
test_doc.set_title("Optimized Reader Test");
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 12.0)
.at(100.0, 700.0)
.write("Test content for optimized reader")?;
test_doc.add_page(page);
let test_path = temp_dir.path().join("optimized_reader_test.pdf");
test_doc.save(&test_path)?;
let memory_configs = vec![
MemoryOptions::default().with_cache_size(10),
MemoryOptions::default().with_cache_size(100),
MemoryOptions::default().with_cache_size(1000),
];
for (i, memory_options) in memory_configs.into_iter().enumerate() {
let pdf_data = fs::read(&test_path)?;
let cursor = Cursor::new(pdf_data);
let parse_options = ParseOptions::default();
let reader_result =
OptimizedPdfReader::new_with_options(cursor, parse_options, memory_options.clone());
if reader_result.is_ok() {
let reader = reader_result.unwrap();
let stats = reader.memory_stats();
assert_eq!(stats.cache_hits, 0); assert_eq!(stats.cache_misses, 0);
assert!(memory_options.cache_size > 0);
println!("Successfully created optimized reader with config {i}");
} else {
println!("Optimized reader creation failed for config {i} (expected)");
}
}
Ok(())
}
#[test]
fn test_memory_manager_integration() -> Result<()> {
let configs = vec![
MemoryOptions::default(),
MemoryOptions::small_file(),
MemoryOptions::large_file(),
MemoryOptions::default().with_cache_size(0), ];
for (i, config) in configs.into_iter().enumerate() {
let manager = MemoryManager::new(config.clone());
let initial_stats = manager.stats();
assert_eq!(initial_stats.allocated_bytes, 0);
assert_eq!(initial_stats.cache_hits, 0);
assert_eq!(initial_stats.cache_misses, 0);
manager.record_allocation(1024);
manager.record_cache_hit();
manager.record_cache_miss();
manager.record_cache_miss();
let updated_stats = manager.stats();
assert_eq!(updated_stats.allocated_bytes, 1024);
assert_eq!(updated_stats.cache_hits, 1);
assert_eq!(updated_stats.cache_misses, 2);
if config.cache_size > 0 {
assert!(manager.cache().is_some());
} else {
assert!(manager.cache().is_none());
}
println!("Memory manager test {i} completed successfully");
}
Ok(())
}
#[test]
fn test_large_document_memory_efficiency() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let mut large_doc = Document::new();
large_doc.set_title("Large Document Memory Efficiency Test");
large_doc.set_compress(true);
let page_count = 25;
for page_num in 1..=page_count {
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 14.0)
.at(50.0, 750.0)
.write(&format!("Large Document - Page {page_num}/{page_count}"))?;
for section in 0..5 {
let section_title = format!("Section {} on page {}", section + 1, page_num);
let y_start = 650.0 - (section as f64 * 120.0);
page.text()
.set_font(Font::HelveticaBold, 12.0)
.at(50.0, y_start)
.write(§ion_title)?;
for line in 0..8 {
let y_pos = y_start - 20.0 - (line as f64 * 12.0);
if y_pos > 50.0 {
page.text()
.set_font(Font::Helvetica, 10.0)
.at(70.0, y_pos)
.write(&format!(
"Content line {} of section {}",
line + 1,
section + 1
))?;
}
}
}
large_doc.add_page(page);
if page_num % 5 == 0 {
let partial_bytes = large_doc.to_bytes()?;
assert!(!partial_bytes.is_empty());
println!(
"Generated {} pages, current size: {} bytes",
page_num,
partial_bytes.len()
);
}
}
let start_time = std::time::Instant::now();
let large_path = temp_dir.path().join("large_memory_efficient.pdf");
large_doc.save(&large_path)?;
let save_duration = start_time.elapsed();
assert!(save_duration.as_secs() < 10);
let file_size = fs::metadata(&large_path).unwrap().len();
assert!(file_size > 20000); assert!(file_size < 10_000_000);
let memory_start = std::time::Instant::now();
let pdf_bytes = large_doc.to_bytes()?;
let memory_duration = memory_start.elapsed();
assert!(!pdf_bytes.is_empty());
assert!(memory_duration.as_secs() < 10);
println!("Large document test completed:");
println!(" Pages: {page_count}");
println!(" File size: {file_size} bytes");
println!(" Save time: {save_duration:?}");
println!(" Memory generation time: {memory_duration:?}");
Ok(())
}
#[test]
fn test_content_type_memory_optimization() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
fn setup_text_heavy(page: &mut Page) -> Result<()> {
for i in 0..50 {
let y_pos = 750.0 - (i as f64 * 15.0);
if y_pos > 50.0 {
page.text()
.set_font(Font::Helvetica, 10.0)
.at(50.0, y_pos)
.write(&format!(
"Text line {} with substantial content for memory testing",
i + 1
))?;
}
}
Ok(())
}
fn setup_graphics_heavy(page: &mut Page) -> Result<()> {
for i in 0..20 {
let x = 50.0 + (i % 10) as f64 * 50.0;
let y = 600.0 - (i / 10) as f64 * 100.0;
page.graphics()
.set_fill_color(oxidize_pdf::graphics::Color::rgb(
(i as f64) / 20.0,
0.5,
1.0 - (i as f64) / 20.0,
))
.rectangle(x, y, 40.0, 40.0)
.fill();
}
Ok(())
}
fn setup_mixed_content(page: &mut Page) -> Result<()> {
page.text()
.set_font(Font::HelveticaBold, 16.0)
.at(50.0, 750.0)
.write("Mixed Content Page")?;
for i in 0..10 {
let y_text = 700.0 - (i as f64 * 60.0);
let y_graphics = y_text - 30.0;
if y_graphics > 50.0 {
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, y_text)
.write(&format!("Mixed content section {}", i + 1))?;
page.graphics()
.set_fill_color(oxidize_pdf::graphics::Color::rgb(0.8, 0.8, 0.9))
.rectangle(50.0, y_graphics, 200.0, 20.0)
.fill();
}
}
Ok(())
}
let content_types: Vec<(&str, fn(&mut Page) -> Result<()>)> = vec![
("text_heavy", setup_text_heavy),
("graphics_heavy", setup_graphics_heavy),
("mixed_content", setup_mixed_content),
];
for (content_type, content_fn) in content_types {
let mut doc = Document::new();
doc.set_title(format!("Memory Test - {content_type}"));
doc.set_compress(true);
for _page_num in 1..=5 {
let mut page = Page::a4();
content_fn(&mut page)?;
doc.add_page(page);
}
let file_path = temp_dir
.path()
.join(format!("memory_content_{content_type}.pdf"));
let start_time = std::time::Instant::now();
doc.save(&file_path)?;
let save_duration = start_time.elapsed();
assert!(file_path.exists());
let file_size = fs::metadata(&file_path).unwrap().len();
let memory_bytes = doc.to_bytes()?;
assert!(!memory_bytes.is_empty());
println!("Content type '{content_type}' test results:");
println!(" File size: {file_size} bytes");
println!(" Save duration: {save_duration:?}");
println!(" Memory size: {} bytes", memory_bytes.len());
assert!(save_duration.as_secs() < 5);
assert!(file_size > 1000); assert!(file_size < 5_000_000); }
Ok(())
}
#[test]
fn test_memory_statistics_tracking() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let mut doc = Document::new();
doc.set_title("Memory Statistics Tracking Test");
let memory_options = MemoryOptions::default().with_cache_size(100);
let manager = MemoryManager::new(memory_options);
let page_count = 10;
for page_num in 1..=page_count {
manager.record_allocation(8192);
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 750.0)
.write(&format!("Statistics tracking page {page_num}"))?;
if page_num % 3 == 0 {
manager.record_cache_hit();
} else {
manager.record_cache_miss();
}
doc.add_page(page);
}
let final_stats = manager.stats();
assert_eq!(final_stats.allocated_bytes, 8192 * page_count as usize);
assert!(final_stats.cache_hits > 0);
assert!(final_stats.cache_misses > 0);
assert_eq!(
final_stats.cache_hits + final_stats.cache_misses,
page_count as usize
);
let stats_path = temp_dir.path().join("memory_statistics.pdf");
doc.save(&stats_path)?;
assert!(stats_path.exists());
let pdf_bytes = doc.to_bytes()?;
assert!(!pdf_bytes.is_empty());
println!("Memory statistics tracking test results:");
println!(" Total allocated: {} bytes", final_stats.allocated_bytes);
println!(" Cache hits: {}", final_stats.cache_hits);
println!(" Cache misses: {}", final_stats.cache_misses);
println!(
" Hit ratio: {:.2}%",
(final_stats.cache_hits as f64
/ (final_stats.cache_hits + final_stats.cache_misses) as f64)
* 100.0
);
Ok(())
}
#[test]
fn test_batch_processing_memory_efficiency() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let batch_size = 5;
let mut generated_files = Vec::new();
for batch_num in 1..=batch_size {
let mut doc = Document::new();
doc.set_title(format!("Batch Document {batch_num}"));
doc.set_compress(true);
for page_num in 1..=batch_num {
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 750.0)
.write(&format!("Batch {batch_num} - Page {page_num}"))?;
for line in 0..(batch_num * 5) {
let y_pos = 700.0 - (line as f64 * 12.0);
if y_pos > 50.0 {
page.text()
.set_font(Font::Helvetica, 10.0)
.at(50.0, y_pos)
.write(&format!("Batch content line {}", line + 1))?;
}
}
doc.add_page(page);
}
let batch_path = temp_dir.path().join(format!("batch_{batch_num}.pdf"));
let start_time = std::time::Instant::now();
doc.save(&batch_path)?;
let save_duration = start_time.elapsed();
assert!(batch_path.exists());
let file_size = fs::metadata(&batch_path).unwrap().len();
generated_files.push((batch_path, file_size, save_duration));
assert!(save_duration.as_secs() < 3); assert!(file_size > (batch_num as u64 * 500)); }
println!("Batch processing results:");
for (i, (path, size, duration)) in generated_files.iter().enumerate() {
println!(" Batch {}: {} bytes in {:?}", i + 1, size, duration);
assert!(path.exists());
}
println!("Batch processing memory efficiency test completed successfully");
Ok(())
}