"""Tests for rendering module (colormaps, tif_to_png).""" import numpy as np import rasterio from rasterio.transform import from_bounds import pytest from pathlib import Path def _make_test_tif(tmp_path, data=None, size=50): """Create a small test GeoTIFF and return its path.""" if data is None: rng = np.random.default_rng(42) data = rng.normal(0, 1, (size, size)).astype(np.float32) transform = from_bounds(660000, 6700000, 661000, 6701000, size, size) tif_file = tmp_path / "test_vis.tif" with rasterio.open( tif_file, 'w', driver='GTiff', height=size, width=size, count=1, dtype='float32', crs='EPSG:2154', transform=transform, compress='lzw' ) as dst: dst.write(data, 1) return tif_file class TestColormaps: def test_colormaps_dict_exists(self): from lidar_pipeline.rendering import COLORMAPS assert isinstance(COLORMAPS, dict) def test_all_viz_steps_have_colormaps(self): """Every VIZ_STEPS entry should have a corresponding COLORMAPS entry or render correctly.""" from lidar_pipeline.pipeline import VIZ_STEPS from lidar_pipeline.rendering import COLORMAPS # Some viz names differ from colormap keys name_map = { 'pos_open': 'positive_openness', 'neg_open': 'negative_openness', } # IGN overlays (ortho, topo) are RGB images — no colormap needed skip = {'ortho', 'topo'} for name, _ in VIZ_STEPS: if name in skip: continue cmap_key = name_map.get(name, name) assert cmap_key in COLORMAPS, f"Missing colormap for: {name} (looked as {cmap_key})" def test_colormap_has_required_keys(self): """Each colormap entry must have cmap, title, legend, description.""" from lidar_pipeline.rendering import COLORMAPS required = {'cmap', 'title', 'legend', 'description'} for name, entry in COLORMAPS.items(): missing = required - set(entry.keys()) assert not missing, f"Colormap '{name}' missing keys: {missing}" class TestTifToPng: def test_converts_tif_to_webp(self, tmp_path): from lidar_pipeline.rendering import tif_to_png tif_file = _make_test_tif(tmp_path) result = tif_to_png(tif_file, tmp_path, 5.0) assert result is not None assert result.exists() assert result.suffix == '.webp' def test_removes_source_tif(self, tmp_path): from lidar_pipeline.rendering import tif_to_png tif_file = _make_test_tif(tmp_path) assert tif_file.exists() tif_to_png(tif_file, tmp_path, 5.0) assert not tif_file.exists(), "Source TIF should be deleted after conversion" def test_webp_has_content(self, tmp_path): from lidar_pipeline.rendering import tif_to_png tif_file = _make_test_tif(tmp_path) result = tif_to_png(tif_file, tmp_path, 5.0) assert result.stat().st_size > 1000 # Must be a real image class TestApplyColormap: def test_symmetric_mode(self, tmp_path): from lidar_pipeline.rendering import COLORMAPS, tif_to_png # LRM uses symmetric mode data = np.random.default_rng(42).normal(0, 0.5, (50, 50)).astype(np.float32) tif_file = _make_test_tif(tmp_path, data) result = tif_to_png(tif_file, tmp_path, 5.0) assert result is not None assert result.exists() def test_percentile_mode(self, tmp_path): from lidar_pipeline.rendering import tif_to_png # Most visualizations use percentile mode data = np.random.default_rng(42).normal(50, 10, (50, 50)).astype(np.float32) tif_file = _make_test_tif(tmp_path, data) result = tif_to_png(tif_file, tmp_path, 5.0) assert result is not None assert result.exists()