use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
fn create_temp_dir(prefix: &str) -> Result<PathBuf, std::io::Error> {
let temp_base = std::env::temp_dir();
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let dir_name = format!("oxigdal_test_{}_{}", prefix, timestamp);
let dir_path = temp_base.join(dir_name);
fs::create_dir_all(&dir_path)?;
Ok(dir_path)
}
fn cleanup_temp_dir(path: &PathBuf) {
let _ = fs::remove_dir_all(path);
}
fn create_stub_tiff(path: &PathBuf) -> Result<(), std::io::Error> {
let mut file = File::create(path)?;
file.write_all(&[0x49, 0x49, 0x2A, 0x00])?;
file.write_all(&[0x08, 0x00, 0x00, 0x00])?;
file.write_all(&[0x00, 0x00])?;
file.write_all(&[0x00, 0x00, 0x00, 0x00])?;
Ok(())
}
fn create_stub_geojson(path: &PathBuf) -> Result<(), std::io::Error> {
let geojson = r#"{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0.0, 0.0]
},
"properties": {}
}
]
}"#;
fs::write(path, geojson)?;
Ok(())
}
mod translate_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_resampling_method_nearest() {
let input = "nearest";
let result = input.to_lowercase();
assert_eq!(result, "nearest");
}
#[test]
fn test_resampling_method_bilinear() {
let input = "bilinear";
let result = input.to_lowercase();
assert_eq!(result, "bilinear");
}
#[test]
fn test_resampling_method_bicubic() {
let input = "bicubic";
let result = input.to_lowercase();
assert_eq!(result, "bicubic");
}
#[test]
fn test_resampling_method_lanczos() {
let input = "lanczos";
let result = input.to_lowercase();
assert_eq!(result, "lanczos");
}
#[test]
fn test_resampling_method_case_insensitive() {
let inputs = ["NEAREST", "Nearest", "BILINEAR", "Bilinear"];
for input in inputs {
let result = input.to_lowercase();
assert!(result == "nearest" || result == "bilinear");
}
}
#[test]
fn test_resampling_method_invalid() {
let input = "invalid_method";
let result = input.to_lowercase();
assert!(
result != "nearest"
&& result != "bilinear"
&& result != "bicubic"
&& result != "lanczos"
);
}
#[test]
fn test_translate_input_not_found() {
let path = PathBuf::from("/nonexistent/path/to/file.tif");
assert!(!path.exists());
}
#[test]
fn test_translate_output_exists_no_overwrite() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("translate_overwrite")?;
let output_path = temp_dir.join("existing_output.tif");
create_stub_tiff(&output_path)?;
assert!(output_path.exists());
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_translate_projwin_validation() {
let projwin = [0.0, 0.0, 10.0, 10.0];
assert_eq!(projwin.len(), 4);
}
#[test]
fn test_translate_projwin_invalid_count() {
let projwin = [0.0, 0.0, 10.0]; assert_ne!(projwin.len(), 4);
}
#[test]
fn test_translate_srcwin_validation() {
let srcwin: Vec<usize> = vec![0, 0, 100, 100];
assert_eq!(srcwin.len(), 4);
}
#[test]
fn test_translate_srcwin_bounds_check() {
let raster_width: u64 = 1000;
let raster_height: u64 = 1000;
let x_off: u64 = 100;
let y_off: u64 = 100;
let width: u64 = 200;
let height: u64 = 200;
assert!(x_off + width <= raster_width);
assert!(y_off + height <= raster_height);
}
#[test]
fn test_translate_srcwin_exceeds_bounds() {
let raster_width: u64 = 1000;
let x_off: u64 = 900;
let width: u64 = 200;
assert!(x_off + width > raster_width);
}
#[test]
fn test_translate_band_selection_valid() {
let bands: Vec<usize> = vec![0, 1, 2];
let num_bands: usize = 4;
for band in &bands {
assert!(*band < num_bands);
}
}
#[test]
fn test_translate_band_selection_invalid() {
let bands: Vec<usize> = vec![0, 1, 5]; let num_bands: usize = 4;
let has_invalid = bands.iter().any(|&b| b >= num_bands);
assert!(has_invalid);
}
#[test]
fn test_translate_output_size_aspect_ratio_width() {
let read_width: u64 = 1000;
let read_height: u64 = 500;
let out_width: u64 = 500;
let aspect = read_height as f64 / read_width as f64;
let out_height = (out_width as f64 * aspect).round() as u64;
assert_eq!(out_height, 250);
}
#[test]
fn test_translate_output_size_aspect_ratio_height() {
let read_width: u64 = 1000;
let read_height: u64 = 500;
let out_height: u64 = 250;
let aspect = read_width as f64 / read_height as f64;
let out_width = (out_height as f64 * aspect).round() as u64;
assert_eq!(out_width, 500);
}
#[test]
fn test_translate_nodata_propagation() {
let input_nodata: Option<f64> = Some(-9999.0);
let user_nodata: Option<f64> = None;
let final_nodata = user_nodata.or(input_nodata);
assert_eq!(final_nodata, Some(-9999.0));
}
#[test]
fn test_translate_nodata_override() {
let input_nodata: Option<f64> = Some(-9999.0);
let user_nodata: Option<f64> = Some(0.0);
let final_nodata = user_nodata.or(input_nodata);
assert_eq!(final_nodata, Some(0.0));
}
}
mod warp_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_warp_parse_epsg_code() {
let crs_str = "EPSG:4326";
let epsg = crs_str.strip_prefix("EPSG:");
assert_eq!(epsg, Some("4326"));
let code: u32 = epsg
.expect("Should have EPSG prefix")
.parse()
.expect("Should parse as u32");
assert_eq!(code, 4326);
}
#[test]
fn test_warp_parse_invalid_epsg() {
let crs_str = "EPSG:invalid";
let epsg_str = crs_str.strip_prefix("EPSG:").expect("Should have prefix");
let result: Result<u32, _> = epsg_str.parse();
assert!(result.is_err());
}
#[test]
fn test_warp_extract_epsg_valid() {
let crs_str = "EPSG:3857";
let result = crs_str
.strip_prefix("EPSG:")
.and_then(|s| s.parse::<u32>().ok());
assert_eq!(result, Some(3857));
}
#[test]
fn test_warp_extract_epsg_invalid() {
let crs_str = "WKT:SOME_WKT_STRING";
let result = crs_str
.strip_prefix("EPSG:")
.and_then(|s| s.parse::<u32>().ok());
assert_eq!(result, None);
}
#[test]
fn test_warp_target_extent_validation() {
let te = [-10000.0, -10000.0, 10000.0, 10000.0];
assert_eq!(te.len(), 4);
assert!(te[0] < te[2]); assert!(te[1] < te[3]); }
#[test]
fn test_warp_target_extent_invalid_bounds() {
let te = [10000.0, 10000.0, -10000.0, -10000.0]; assert!(te[0] > te[2]); }
#[test]
fn test_warp_resolution_from_size() {
let min_x = 0.0;
let max_x = 1000.0;
let min_y = 0.0;
let max_y = 500.0;
let ts_x: usize = 1000;
let ts_y: usize = 500;
let pixel_width = (max_x - min_x) / ts_x as f64;
let pixel_height = -(max_y - min_y) / ts_y as f64;
assert!((pixel_width - 1.0).abs() < 1e-10);
assert!((pixel_height - (-1.0)).abs() < 1e-10);
}
#[test]
fn test_warp_resampling_methods() {
let methods = ["nearest", "bilinear", "bicubic", "lanczos"];
for method in methods {
assert!(
method == "nearest"
|| method == "bilinear"
|| method == "bicubic"
|| method == "lanczos"
);
}
}
#[test]
fn test_warp_input_validation() {
let nonexistent = PathBuf::from("/path/to/nonexistent.tif");
assert!(!nonexistent.exists());
}
#[test]
fn test_warp_output_overwrite() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("warp_overwrite")?;
let output_path = temp_dir.join("output.tif");
create_stub_tiff(&output_path)?;
let overwrite = false;
assert!(output_path.exists() && !overwrite);
cleanup_temp_dir(&temp_dir);
Ok(())
}
}
mod calc_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_calc_datatype_uint8() {
let dtype = "uint8";
assert!(dtype.to_lowercase() == "uint8" || dtype.to_lowercase() == "byte");
}
#[test]
fn test_calc_datatype_float32() {
let dtype = "float32";
assert_eq!(dtype.to_lowercase(), "float32");
}
#[test]
fn test_calc_datatype_invalid() {
let dtype = "complex128";
let valid_types = [
"uint8", "uint16", "uint32", "int16", "int32", "float32", "float64",
];
assert!(!valid_types.contains(&dtype.to_lowercase().as_str()));
}
#[test]
fn test_calc_expression_single_var() {
let expr = "A * 2.0";
assert!(expr.contains('A'));
}
#[test]
fn test_calc_expression_multiple_vars() {
let expr = "(A - B) / (A + B)";
assert!(expr.contains('A') && expr.contains('B'));
}
#[test]
fn test_calc_expression_ndvi() {
let expr = "(A - B) / (A + B)";
assert!(expr.contains("A - B") && expr.contains("A + B"));
}
#[test]
fn test_calc_input_collection() {
let input_a: Option<PathBuf> = Some(PathBuf::from("a.tif"));
let input_b: Option<PathBuf> = Some(PathBuf::from("b.tif"));
let input_c: Option<PathBuf> = None;
let mut inputs = Vec::new();
if let Some(p) = input_a {
inputs.push(p);
}
if let Some(p) = input_b {
inputs.push(p);
}
if let Some(p) = input_c {
inputs.push(p);
}
assert_eq!(inputs.len(), 2);
}
#[test]
fn test_calc_empty_inputs() {
let inputs: Vec<PathBuf> = Vec::new();
assert!(inputs.is_empty());
}
#[test]
fn test_calc_band_index_validation() {
let band: u32 = 0;
let total_bands: u32 = 4;
assert!(band < total_bands);
}
#[test]
fn test_calc_band_index_out_of_range() {
let band: u32 = 5;
let total_bands: u32 = 4;
assert!(band >= total_bands);
}
#[test]
fn test_calc_dimension_match() {
let dims_a = (1000u64, 1000u64);
let dims_b = (1000u64, 1000u64);
assert_eq!(dims_a, dims_b);
}
#[test]
fn test_calc_dimension_mismatch() {
let dims_a = (1000u64, 1000u64);
let dims_b = (500u64, 500u64);
assert_ne!(dims_a, dims_b);
}
#[test]
fn test_calc_expression_math_functions() {
let expressions = ["sqrt(A)", "log(A)", "exp(A)", "sin(A)", "cos(A)", "abs(A)"];
for expr in expressions {
assert!(expr.contains('A'));
}
}
}
mod buildvrt_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_buildvrt_output_overwrite() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("buildvrt_test")?;
let output_path = temp_dir.join("output.vrt");
fs::write(&output_path, "dummy vrt content")?;
let overwrite = false;
assert!(output_path.exists() && !overwrite);
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_buildvrt_input_existence() {
let inputs: Vec<PathBuf> = vec![
PathBuf::from("/nonexistent/file1.tif"),
PathBuf::from("/nonexistent/file2.tif"),
];
for input in &inputs {
assert!(!input.exists());
}
}
#[test]
fn test_buildvrt_multiple_inputs() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("buildvrt_multi")?;
let input1 = temp_dir.join("input1.tif");
let input2 = temp_dir.join("input2.tif");
create_stub_tiff(&input1)?;
create_stub_tiff(&input2)?;
assert!(input1.exists());
assert!(input2.exists());
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_buildvrt_resolution() {
let resolution: Option<f64> = Some(10.0);
assert!(resolution.is_some());
assert_eq!(resolution, Some(10.0));
}
#[test]
fn test_buildvrt_epsg() {
let epsg: Option<u32> = Some(4326);
assert!(epsg.is_some());
assert_eq!(epsg, Some(4326));
}
}
mod merge_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_merge_minimum_inputs() {
let inputs = [PathBuf::from("single.tif")];
assert!(inputs.len() < 2);
}
#[test]
fn test_merge_valid_input_count() {
let inputs = [
PathBuf::from("input1.tif"),
PathBuf::from("input2.tif"),
PathBuf::from("input3.tif"),
];
assert!(inputs.len() >= 2);
}
#[test]
fn test_merge_band_compatibility() {
let bands_input1: u32 = 3;
let bands_input2: u32 = 3;
let bands_input3: u32 = 1;
assert_eq!(bands_input1, bands_input2);
assert_ne!(bands_input1, bands_input3);
}
#[test]
fn test_merge_extent_min() {
let extents = [
(0.0, 0.0, 100.0, 100.0),
(50.0, 50.0, 150.0, 150.0),
(-50.0, -50.0, 50.0, 50.0),
];
let min_x = extents.iter().map(|e| e.0).fold(f64::INFINITY, f64::min);
let min_y = extents.iter().map(|e| e.1).fold(f64::INFINITY, f64::min);
assert_eq!(min_x, -50.0);
assert_eq!(min_y, -50.0);
}
#[test]
fn test_merge_extent_max() {
let extents = [
(0.0, 0.0, 100.0, 100.0),
(50.0, 50.0, 150.0, 150.0),
(-50.0, -50.0, 50.0, 50.0),
];
let max_x = extents
.iter()
.map(|e| e.2)
.fold(f64::NEG_INFINITY, f64::max);
let max_y = extents
.iter()
.map(|e| e.3)
.fold(f64::NEG_INFINITY, f64::max);
assert_eq!(max_x, 150.0);
assert_eq!(max_y, 150.0);
}
#[test]
fn test_merge_finest_resolution() {
let resolutions = [10.0, 5.0, 20.0, 2.5];
let finest = resolutions.iter().fold(f64::INFINITY, |a, &b| a.min(b));
assert_eq!(finest, 2.5);
}
#[test]
fn test_merge_nodata_handling() {
let input_nodata: Option<f64> = Some(-9999.0);
let output_nodata: Option<f64> = Some(0.0);
assert!(input_nodata.is_some());
assert!(output_nodata.is_some());
assert_ne!(input_nodata, output_nodata);
}
#[test]
fn test_merge_epsg_compatibility() {
let target_epsg: Option<u32> = Some(4326);
let input_epsg: Option<u32> = Some(4326);
assert_eq!(target_epsg, input_epsg);
}
#[test]
fn test_merge_epsg_incompatibility() {
let target_epsg: Option<u32> = Some(4326);
let input_epsg: Option<u32> = Some(3857);
assert_ne!(target_epsg, input_epsg);
}
}
mod inspect_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_inspect_input_path() {
let input: String = "/path/to/file.tif".to_string();
assert!(!input.is_empty());
}
#[test]
fn test_inspect_format_options() {
let formats = ["text", "json"];
for format in formats {
assert!(format == "text" || format == "json");
}
}
#[test]
fn test_inspect_detailed_flag() {
let detailed = true;
assert!(detailed);
}
}
mod integration_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_temp_dir_lifecycle() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("lifecycle_test")?;
assert!(temp_dir.exists());
cleanup_temp_dir(&temp_dir);
assert!(!temp_dir.exists());
Ok(())
}
#[test]
fn test_create_stub_tiff() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("stub_tiff_test")?;
let tiff_path = temp_dir.join("test.tif");
create_stub_tiff(&tiff_path)?;
assert!(tiff_path.exists());
let metadata = fs::metadata(&tiff_path)?;
assert!(metadata.len() > 0);
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_create_stub_geojson() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("stub_geojson_test")?;
let geojson_path = temp_dir.join("test.geojson");
create_stub_geojson(&geojson_path)?;
assert!(geojson_path.exists());
let content = fs::read_to_string(&geojson_path)?;
assert!(content.contains("FeatureCollection"));
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_multiple_temp_files() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("multi_file_test")?;
for i in 0..5 {
let path = temp_dir.join(format!("file_{}.tif", i));
create_stub_tiff(&path)?;
assert!(path.exists());
}
let entries: Vec<_> = fs::read_dir(&temp_dir)?.collect();
assert_eq!(entries.len(), 5);
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_file_extension_detection() {
let paths: [(&str, Option<&str>); 4] = [
("test.tif", Some("tif")),
("test.geojson", Some("geojson")),
("test.shp", Some("shp")),
("test", None),
];
for (path_str, expected_ext) in paths {
let path = PathBuf::from(path_str);
let ext = path.extension().and_then(|e: &std::ffi::OsStr| e.to_str());
assert_eq!(ext, expected_ext);
}
}
#[test]
fn test_path_manipulation() {
let base = PathBuf::from("/data/input.tif");
let output = base.with_extension("output.tif");
assert!(output.to_string_lossy().ends_with("output.tif"));
}
}
mod error_handling_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_file_not_found() {
let path = PathBuf::from("/definitely/not/a/real/path/file.tif");
let result = fs::metadata(&path);
assert!(result.is_err());
}
#[test]
fn test_invalid_path() {
let path = PathBuf::from("");
assert!(path.as_os_str().is_empty());
}
#[test]
fn test_file_permissions() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("permissions_test")?;
let path = temp_dir.join("test.tif");
create_stub_tiff(&path)?;
let metadata = fs::metadata(&path)?;
assert!(!metadata.permissions().readonly());
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_empty_file_handling() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("empty_file_test")?;
let path = temp_dir.join("empty.tif");
File::create(&path)?;
let metadata = fs::metadata(&path)?;
assert_eq!(metadata.len(), 0);
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_result_propagation() -> Result<(), Box<dyn std::error::Error>> {
fn inner_function() -> Result<u32, Box<dyn std::error::Error>> {
let value: u32 = "42".parse()?;
Ok(value)
}
let result = inner_function()?;
assert_eq!(result, 42);
Ok(())
}
#[test]
fn test_option_handling() -> Result<(), Box<dyn std::error::Error>> {
let value: Option<i32> = Some(42);
let result = value.ok_or_else(|| std::io::Error::other("Value is None"))?;
assert_eq!(result, 42);
Ok(())
}
#[test]
fn test_error_context() {
let result: Result<i32, String> = Err("Original error".to_string());
let with_context = result.map_err(|e| format!("Context: {}", e));
assert!(with_context.is_err());
let err_msg = with_context.expect_err("Should have error");
assert!(err_msg.contains("Context"));
}
}
mod utility_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_format_detection_geotiff() {
let extensions = ["tif", "tiff", "TIF", "TIFF"];
for ext in extensions {
assert!(ext.to_lowercase() == "tif" || ext.to_lowercase() == "tiff");
}
}
#[test]
fn test_format_detection_geojson() {
let extensions = ["json", "geojson", "JSON", "GeoJSON"];
for ext in extensions {
let lower = ext.to_lowercase();
assert!(lower == "json" || lower == "geojson");
}
}
#[test]
fn test_format_detection_shapefile() {
let ext = "shp";
assert_eq!(ext.to_lowercase(), "shp");
}
#[test]
fn test_format_detection_flatgeobuf() {
let ext = "fgb";
assert_eq!(ext.to_lowercase(), "fgb");
}
#[test]
fn test_format_size_bytes() {
let bytes: u64 = 512;
let formatted = format!("{} B", bytes);
assert_eq!(formatted, "512 B");
}
#[test]
fn test_format_size_kilobytes() {
let bytes: u64 = 1024;
let size_kb = bytes as f64 / 1024.0;
let formatted = format!("{:.2} KB", size_kb);
assert_eq!(formatted, "1.00 KB");
}
#[test]
fn test_format_size_megabytes() {
let bytes: u64 = 1_048_576; let size_mb = bytes as f64 / 1024.0 / 1024.0;
let formatted = format!("{:.2} MB", size_mb);
assert_eq!(formatted, "1.00 MB");
}
#[test]
fn test_format_size_gigabytes() {
let bytes: u64 = 1_073_741_824; let size_gb = bytes as f64 / 1024.0 / 1024.0 / 1024.0;
let formatted = format!("{:.2} GB", size_gb);
assert_eq!(formatted, "1.00 GB");
}
}
mod geotransform_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_geotransform_subset() {
let origin_x = 0.0;
let origin_y = 100.0;
let pixel_width = 1.0;
let pixel_height = -1.0;
let x_offset: u64 = 10;
let y_offset: u64 = 5;
let new_origin_x = origin_x + (x_offset as f64 * pixel_width);
let new_origin_y = origin_y + (y_offset as f64 * pixel_height);
assert_eq!(new_origin_x, 10.0);
assert_eq!(new_origin_y, 95.0);
}
#[test]
fn test_pixel_to_geo() {
let origin_x = 0.0;
let origin_y = 100.0;
let pixel_width = 1.0;
let pixel_height = -1.0;
let px = 50.0;
let py = 25.0;
let geo_x = origin_x + px * pixel_width;
let geo_y = origin_y + py * pixel_height;
assert_eq!(geo_x, 50.0);
assert_eq!(geo_y, 75.0);
}
#[test]
fn test_geo_to_pixel() {
let origin_x = 0.0;
let origin_y = 100.0;
let pixel_width = 1.0;
let pixel_height = -1.0;
let geo_x = 50.0;
let geo_y = 75.0;
let px = (geo_x - origin_x) / pixel_width;
let py = (geo_y - origin_y) / pixel_height;
assert_eq!(px, 50.0);
assert_eq!(py, 25.0);
}
#[test]
fn test_geotransform_scale() {
let pixel_width = 1.0;
let pixel_height = -1.0;
let read_width: u64 = 1000;
let read_height: u64 = 1000;
let out_width: u64 = 500;
let out_height: u64 = 500;
let scale_x = read_width as f64 / out_width as f64;
let scale_y = read_height as f64 / out_height as f64;
let new_pixel_width = pixel_width * scale_x;
let new_pixel_height = pixel_height * scale_y;
assert_eq!(new_pixel_width, 2.0);
assert_eq!(new_pixel_height, -2.0);
}
#[test]
fn test_geotransform_determinant() {
let pixel_width = 1.0;
let pixel_height = -1.0;
let row_rotation = 0.0;
let col_rotation = 0.0;
let det: f64 = pixel_width * pixel_height - row_rotation * col_rotation;
assert!((det - (-1.0)).abs() < 1e-10);
assert!(det.abs() > 1e-10); }
#[test]
fn test_geotransform_zero_determinant() {
let pixel_width = 1.0;
let pixel_height = 0.0; let row_rotation = 0.0;
let col_rotation = 0.0;
let det: f64 = pixel_width * pixel_height - row_rotation * col_rotation;
assert!(det.abs() < 1e-10); }
}
mod buffer_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_buffer_size_calculation() {
let width: u64 = 1000;
let height: u64 = 1000;
let bytes_per_pixel: u64 = 8;
let total_bytes = width * height * bytes_per_pixel;
assert_eq!(total_bytes, 8_000_000);
}
#[test]
fn test_multiband_buffer_size() {
let width: u64 = 1000;
let height: u64 = 1000;
let bands: usize = 3;
let bytes_per_pixel: u64 = 4;
let total_bytes = (width * height * bytes_per_pixel) * bands as u64;
assert_eq!(total_bytes, 12_000_000);
}
#[test]
fn test_pixel_index() {
let width: u64 = 1000;
let x: u64 = 100;
let y: u64 = 200;
let index = y * width + x;
assert_eq!(index, 200_100);
}
#[test]
fn test_pixel_bounds_check() {
let width: u64 = 1000;
let height: u64 = 1000;
let valid_x: u64 = 500;
let valid_y: u64 = 500;
let invalid_x: u64 = 1000;
let invalid_y: u64 = 1000;
assert!(valid_x < width && valid_y < height);
assert!(!(invalid_x < width && invalid_y < height));
}
#[test]
fn test_nodata_comparison() {
let nodata = -9999.0f64;
let value = -9999.0f64;
let valid_value = 42.0f64;
assert!((value - nodata).abs() < f64::EPSILON);
assert!((valid_value - nodata).abs() > f64::EPSILON);
}
#[test]
fn test_float_to_bytes() {
let value: f64 = 42.0;
let bytes = value.to_le_bytes();
assert_eq!(bytes.len(), 8);
let restored = f64::from_le_bytes(bytes);
assert!((restored - value).abs() < f64::EPSILON);
}
}
mod crs_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_epsg_wgs84() {
let epsg: u32 = 4326;
assert_eq!(epsg, 4326);
}
#[test]
fn test_epsg_web_mercator() {
let epsg: u32 = 3857;
assert_eq!(epsg, 3857);
}
#[test]
fn test_epsg_string_parsing() {
let crs_str = "EPSG:4326";
let parts: Vec<&str> = crs_str.split(':').collect();
assert_eq!(parts.len(), 2);
assert_eq!(parts[0], "EPSG");
assert_eq!(parts[1], "4326");
}
#[test]
fn test_coordinate_bounds_wgs84() {
let lon = 180.0;
let lat = 90.0;
assert!((-180.0..=180.0).contains(&lon));
assert!((-90.0..=90.0).contains(&lat));
}
#[test]
fn test_coordinate_bounds_invalid() {
let lon = 200.0;
let lat = 100.0;
assert!(lon > 180.0);
assert!(lat > 90.0);
}
}
mod cli_parsing_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_output_format_text() {
let format = "text";
assert_eq!(format.to_lowercase(), "text");
}
#[test]
fn test_output_format_json() {
let format = "json";
assert_eq!(format.to_lowercase(), "json");
}
#[test]
fn test_output_format_case_insensitive() {
let formats = ["TEXT", "Text", "JSON", "Json"];
for format in formats {
let lower = format.to_lowercase();
assert!(lower == "text" || lower == "json");
}
}
#[test]
fn test_output_format_invalid() {
let format = "xml";
let valid = format.to_lowercase() == "text" || format.to_lowercase() == "json";
assert!(!valid);
}
#[test]
fn test_verbose_quiet_flags() {
let verbose = true;
let quiet = false;
assert!(!verbose || !quiet);
}
#[test]
fn test_global_flags() {
let global_flags = ["--verbose", "--quiet", "--format"];
assert_eq!(global_flags.len(), 3);
}
#[test]
fn test_subcommand_names() {
let subcommands = [
"info",
"convert",
"translate",
"warp",
"calc",
"build-vrt",
"merge",
"validate",
"inspect",
];
assert!(subcommands.len() >= 9);
}
}
mod progress_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_progress_bar_total() {
let bands: u64 = 4;
let total = bands;
assert_eq!(total, 4);
}
#[test]
fn test_progress_increment() {
let mut current: u64 = 0;
let total: u64 = 10;
for _ in 0..total {
current += 1;
}
assert_eq!(current, total);
}
#[test]
fn test_progress_percentage() {
let current: u64 = 50;
let total: u64 = 100;
let percentage = (current as f64 / total as f64) * 100.0;
assert_eq!(percentage, 50.0);
}
}
mod json_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_simple_serialization() -> Result<(), Box<dyn std::error::Error>> {
#[derive(serde::Serialize)]
struct TestStruct {
name: String,
value: i32,
}
let test = TestStruct {
name: "test".to_string(),
value: 42,
};
let json = serde_json::to_string(&test)?;
assert!(json.contains("test"));
assert!(json.contains("42"));
Ok(())
}
#[test]
fn test_optional_field_serialization() -> Result<(), Box<dyn std::error::Error>> {
#[derive(serde::Serialize)]
struct TestStruct {
#[serde(skip_serializing_if = "Option::is_none")]
optional: Option<String>,
}
let with_value = TestStruct {
optional: Some("value".to_string()),
};
let without_value = TestStruct { optional: None };
let json_with = serde_json::to_string(&with_value)?;
let json_without = serde_json::to_string(&without_value)?;
assert!(json_with.contains("optional"));
assert!(!json_without.contains("optional"));
Ok(())
}
#[test]
fn test_pretty_print_json() -> Result<(), Box<dyn std::error::Error>> {
#[derive(serde::Serialize)]
struct TestStruct {
field: String,
}
let test = TestStruct {
field: "value".to_string(),
};
let json = serde_json::to_string_pretty(&test)?;
assert!(json.contains('\n')); Ok(())
}
}
mod parallel_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_cpu_count() {
let cpus = num_cpus::get();
assert!(cpus >= 1);
}
#[test]
fn test_work_division() {
let total_work: usize = 100;
let num_threads: usize = 4;
let work_per_thread = total_work / num_threads;
let remainder = total_work % num_threads;
assert_eq!(work_per_thread, 25);
assert_eq!(remainder, 0);
}
#[test]
fn test_uneven_work_division() {
let total_work: usize = 103;
let num_threads: usize = 4;
let work_per_thread = total_work / num_threads;
let remainder = total_work % num_threads;
assert_eq!(work_per_thread, 25);
assert_eq!(remainder, 3);
}
}
mod edge_case_tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_zero_dimension() {
let width: u64 = 0;
let height: u64 = 100;
let is_valid = width > 0 && height > 0;
assert!(!is_valid);
}
#[test]
fn test_large_dimension() {
let width: u64 = 1_000_000;
let height: u64 = 1_000_000;
let bytes_per_pixel: u64 = 8;
let total_bytes = width
.checked_mul(height)
.and_then(|n| n.checked_mul(bytes_per_pixel));
assert!(total_bytes.is_some());
}
#[test]
fn test_nan_handling() {
let value = f64::NAN;
assert!(value.is_nan());
let is_valid = !value.is_nan() && !value.is_infinite();
assert!(!is_valid);
}
#[test]
fn test_infinity_handling() {
let value = f64::INFINITY;
assert!(value.is_infinite());
let is_valid = !value.is_nan() && !value.is_infinite();
assert!(!is_valid);
}
#[test]
fn test_negative_resolution() {
let pixel_height = -1.0f64;
assert!(pixel_height < 0.0);
assert!((pixel_height.abs() - 1.0).abs() < f64::EPSILON);
}
#[test]
fn test_unicode_path() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("unicode_test")?;
let unicode_name = temp_dir.join("test_file_with_unicode.tif");
create_stub_tiff(&unicode_name)?;
assert!(unicode_name.exists());
cleanup_temp_dir(&temp_dir);
Ok(())
}
#[test]
fn test_path_with_spaces() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = create_temp_dir("space test dir")?;
let file_path = temp_dir.join("file with spaces.tif");
create_stub_tiff(&file_path)?;
assert!(file_path.exists());
assert!(file_path.to_string_lossy().contains(' '));
cleanup_temp_dir(&temp_dir);
Ok(())
}
}