"""Unit tests for src.analysis.geometry_analyzer.""" from __future__ import annotations import numpy as np from src.analysis.geometry_analyzer import GeometryAnalyzer def _blank_page(w: int = 400, h: int = 300) -> np.ndarray: return np.full((h, w, 3), 250, dtype=np.uint8) def _draw_ink_blob(image: np.ndarray, box) -> np.ndarray: out = image.copy() x1, y1, x2, y2 = box out[y1:y2, x1:x2] = 30 # dark ink blob return out def test_geometry_analyzer_detects_ink_inside_target_box(): page = _blank_page() target_box = (100, 100, 200, 150) page = _draw_ink_blob(page, (120, 115, 180, 135)) analyzer = GeometryAnalyzer() result = analyzer.analyze(page, target_box=target_box, canvas_size=(400, 300)) assert result.found is True assert result.ink_pixels > 0 assert result.box_coverage_pct > 0 assert result.containment_pct > 0 def test_geometry_analyzer_reports_not_found_on_blank_box(): page = _blank_page() target_box = (100, 100, 200, 150) analyzer = GeometryAnalyzer() result = analyzer.analyze(page, target_box=target_box, canvas_size=(400, 300)) assert result.found is False assert result.ink_pixels == 0 assert result.box_coverage_pct == 0.0 assert result.polygon_overlap_proxy == 0.0 def test_geometry_analyzer_ignores_printed_rule_line(): """A thin, wide, solid horizontal line (a printed field rule) should not register as ink.""" page = _blank_page() target_box = (50, 50, 350, 70) page[64:66, 60:340] = 40 # printed underline: 2px tall, very wide, solid analyzer = GeometryAnalyzer() result = analyzer.analyze(page, target_box=target_box, canvas_size=(400, 300)) assert result.found is False def test_geometry_analyzer_full_coverage_signature(): page = _blank_page() target_box = (100, 100, 200, 150) page = _draw_ink_blob(page, (100, 100, 200, 150)) # fills the entire box analyzer = GeometryAnalyzer() result = analyzer.analyze(page, target_box=target_box, canvas_size=(400, 300)) assert result.found is True assert result.box_coverage_pct > 95.0 def test_geometry_analyzer_widens_search_with_placement_hint(): page = _blank_page() target_box = (100, 100, 150, 120) # Ink is well outside the tight target box (simulating a misplaced signature) # but within the placement hint bbox used to widen the search. hint_bbox = (250, 100, 320, 150) page = _draw_ink_blob(page, (260, 110, 300, 140)) analyzer = GeometryAnalyzer() result = analyzer.analyze(page, target_box=target_box, canvas_size=(400, 300), placement_hint_bbox=hint_bbox) assert result.found is True # Detected ink should be near the hint location, not the empty target box. assert result.signature_bbox_px[0] > 200 def test_geometry_result_no_nan_when_not_found(): import math page = _blank_page() analyzer = GeometryAnalyzer() result = analyzer.analyze(page, target_box=(10, 10, 50, 30), canvas_size=(400, 300)) assert not math.isnan(result.mean_dark_intensity) assert not math.isnan(result.gray_stddev)