use crate::utils::ExtractedTarget; use std::fs; use std::path::Path; use tempfile::tempdir; use crate::utils::{CmprssInput, CmprssOutput, CompressionLevelValidator, Compressor, Result}; /// Test basic trait functionality that should be common across all compressors pub fn test_compressor_interface( compressor: &T, expected_name: &str, expected_extension: Option<&str>, ) { let ext = expected_extension.unwrap_or(expected_name); // Test name() returns expected value assert_eq!(compressor.name(), expected_name); // Test extension() returns expected value assert_eq!(compressor.extension(), ext); // Test is_archive() detection logic let temp_dir = tempdir().expect("Failed to create temp dir"); // Test with matching extension let archive_path = temp_dir.path().join(format!("test.{}", ext)); fs::File::create(&archive_path).expect("Failed to create test file"); assert!(compressor.is_archive(&archive_path)); // Test with non-matching extension let non_archive_path = temp_dir.path().join("test.txt"); fs::File::create(&non_archive_path).expect("Failed to create test file"); assert!(!compressor.is_archive(&non_archive_path)); // Test default_compressed_filename let test_path = Path::new("test.txt"); let expected = format!("test.txt.{}", ext); assert_eq!(compressor.default_compressed_filename(test_path), expected); // Test default_extracted_filename let formatted_name = format!("test.{}", ext); let archive_path = Path::new(&formatted_name); match compressor.default_extracted_target() { ExtractedTarget::File => { assert_eq!(compressor.default_extracted_filename(archive_path), "test"); } ExtractedTarget::Directory => { assert_eq!(compressor.default_extracted_filename(archive_path), "."); } } // Test default_extracted_filename with non-matching extension let non_archive_path = Path::new("test.txt"); match compressor.default_extracted_target() { ExtractedTarget::File => { assert_eq!( compressor.default_extracted_filename(non_archive_path), "archive" ); } ExtractedTarget::Directory => { assert_eq!(compressor.default_extracted_filename(non_archive_path), "."); } } } /// Test compression and extraction functionality with a simple string pub fn test_compressor_roundtrip(compressor: &T, test_data: &str) -> Result { let temp_dir = tempdir().expect("Failed to create temp dir"); // Create test file let input_path = temp_dir.path().join("input.txt"); fs::write(&input_path, test_data)?; // Compress let archive_path = temp_dir .path() .join(format!("archive.{}", compressor.extension())); compressor.compress( CmprssInput::Path(vec![input_path.clone()]), CmprssOutput::Path(archive_path.clone()), )?; // Extract let output_path = match compressor.default_extracted_target() { ExtractedTarget::File => temp_dir.path().join("output.txt"), ExtractedTarget::Directory => temp_dir.path().join("output"), }; compressor.extract( CmprssInput::Path(vec![archive_path]), CmprssOutput::Path(output_path.clone()), )?; // Verify let input_filename = "input.txt"; let output_data = match compressor.default_extracted_target() { ExtractedTarget::File => fs::read_to_string(output_path)?, ExtractedTarget::Directory => fs::read_to_string(output_path.join(input_filename))?, }; assert_eq!(output_data, test_data); Ok(()) } /// Test compression and extraction with different content sizes pub fn test_compression(compressor: &T) -> Result { // Test with empty content test_compressor_roundtrip(compressor, "")?; // Test with small content test_compressor_roundtrip(compressor, "Small test content")?; // Test with medium content (generate a 10KB string) let medium_content = "0123456789".repeat(1024); test_compressor_roundtrip(compressor, &medium_content)?; Ok(()) } /// Run a full suite of tests on a compressor implementation pub fn run_compressor_tests( compressor: &T, expected_name: &str, expected_extension: Option<&str>, ) -> Result { // Test interface methods test_compressor_interface(compressor, expected_name, expected_extension); // Test compression/extraction functionality test_compression(compressor)?; Ok(()) } /// Helper function to test CompressionValidator implementations /// This avoids duplicating the same test pattern across multiple backends pub fn test_compression_validator_helper( validator: &V, min_level: i32, max_level: i32, default_level: i32, fast_name_level: Option, best_name_level: Option, none_name_level: Option, ) { // Test range assert_eq!(validator.min_level(), min_level); assert_eq!(validator.max_level(), max_level); assert_eq!(validator.default_level(), default_level); // Test validation assert!(validator.is_valid_level(min_level)); assert!(validator.is_valid_level(max_level)); assert!(!validator.is_valid_level(min_level - 1)); assert!(!validator.is_valid_level(max_level + 1)); // Test middle level if range is big enough if max_level - min_level >= 2 { let mid_level = (min_level + max_level) / 2; assert!(validator.is_valid_level(mid_level)); } // Test clamping assert_eq!(validator.validate_and_clamp_level(min_level - 1), min_level); assert_eq!(validator.validate_and_clamp_level(min_level), min_level); assert_eq!(validator.validate_and_clamp_level(max_level), max_level); assert_eq!(validator.validate_and_clamp_level(max_level + 1), max_level); // Test special names if let Some(level) = fast_name_level { assert_eq!(validator.name_to_level("fast"), Some(level)); } else { assert_eq!(validator.name_to_level("fast"), None); } if let Some(level) = best_name_level { assert_eq!(validator.name_to_level("best"), Some(level)); } else { assert_eq!(validator.name_to_level("best"), None); } if let Some(level) = none_name_level { assert_eq!(validator.name_to_level("none"), Some(level)); } else { assert_eq!(validator.name_to_level("none"), None); } // Test invalid name assert_eq!(validator.name_to_level("invalid"), None); }