Venket / tests /test_difficulty_analyzer.py
venki101's picture
Add files using upload-large-folder tool
1a12965 verified
Raw History Blame Contribute Delete
4.2 kB
"""Unit tests for src.analysis.difficulty_analyzer -- verifies the exact spec formula."""
from __future__ import annotations
import math
from src.analysis.difficulty_analyzer import DifficultyAnalyzer
from src.analysis.geometry_analyzer import GeometryResult
from src.config.schema import DifficultyWeights
def _geometry(
polygon_overlap_proxy: float, whitespace_ratio: float, mean_dark_intensity: float,
signature_area_ratio: float, gray_stddev: float,
) -> GeometryResult:
box = (0.0, 0.0, 100.0, 20.0)
return GeometryResult(
found=True, signature_bbox_px=box, min_rotated_rect={}, signature_polygon_px=[[0, 0], [1, 0], [1, 1]],
convex_hull_px=[], centroid_px=(50.0, 10.0), orientation_deg=0.0, ink_pixels=100,
target_box=box, actual_box=box, search_box=box,
box_coverage_pct=0.0, containment_pct=0.0, ink_fill_ratio=1 - whitespace_ratio,
polygon_overlap_proxy=polygon_overlap_proxy, distance_from_box_center_px=0.0, margin_utilization=0.0,
mean_dark_intensity=mean_dark_intensity, gray_stddev=gray_stddev,
signature_area_ratio=signature_area_ratio, whitespace_ratio=whitespace_ratio,
)
def test_difficulty_score_matches_exact_spec_formula():
weights = DifficultyWeights()
analyzer = DifficultyAnalyzer(weights)
geom = _geometry(
polygon_overlap_proxy=0.4, # misplacement = 0.6
whitespace_ratio=0.5, # whitespace = 0.5
mean_dark_intensity=150.0, # faintness = clamp((150-90)/120) = 0.5
signature_area_ratio=0.02, # smallness = clamp(1 - 0.02/0.04) = 0.5
gray_stddev=30.0, # low_texture = clamp(1 - 30/60) = 0.5
)
result = analyzer.analyze(geom)
assert math.isclose(result.misplacement, 0.6, abs_tol=1e-6)
assert math.isclose(result.whitespace, 0.5, abs_tol=1e-6)
assert math.isclose(result.faintness, 0.5, abs_tol=1e-6)
assert math.isclose(result.smallness, 0.5, abs_tol=1e-6)
assert math.isclose(result.low_texture, 0.5, abs_tol=1e-6)
expected = 0.35 * 0.6 + 0.20 * 0.5 + 0.20 * 0.5 + 0.15 * 0.5 + 0.10 * 0.5
assert math.isclose(result.score, expected, abs_tol=1e-6)
def test_difficulty_components_are_clamped_to_unit_interval():
weights = DifficultyWeights()
analyzer = DifficultyAnalyzer(weights)
geom = _geometry(
polygon_overlap_proxy=-5.0, # misplacement would exceed 1 -> clamp to 1
whitespace_ratio=2.0, # clamp to 1
mean_dark_intensity=1000.0, # faintness clamp to 1
signature_area_ratio=-1.0, # smallness clamp to 1
gray_stddev=-10.0, # low_texture clamp to 1
)
result = analyzer.analyze(geom)
assert result.misplacement == 1.0
assert result.whitespace == 1.0
assert result.faintness == 1.0
assert result.smallness == 1.0
assert result.low_texture == 1.0
assert result.score == 1.0
assert result.tier == "hard"
def test_difficulty_tier_boundaries():
weights = DifficultyWeights()
analyzer = DifficultyAnalyzer(weights)
# All-zero components -> score 0 -> easy.
geom_easy = _geometry(
polygon_overlap_proxy=1.0, whitespace_ratio=0.0, mean_dark_intensity=90.0,
signature_area_ratio=0.04, gray_stddev=60.0,
)
assert analyzer.analyze(geom_easy).tier == "easy"
assert analyzer.analyze(geom_easy).score == 0.0
# All components at 1.0 -> score 1.0 -> hard.
geom_hard = _geometry(
polygon_overlap_proxy=0.0, whitespace_ratio=1.0, mean_dark_intensity=300.0,
signature_area_ratio=0.0, gray_stddev=0.0,
)
assert analyzer.analyze(geom_hard).tier == "hard"
def test_difficulty_score_is_reproducible_given_same_geometry():
weights = DifficultyWeights()
analyzer = DifficultyAnalyzer(weights)
geom = _geometry(0.3, 0.4, 140.0, 0.01, 25.0)
r1 = analyzer.analyze(geom)
r2 = analyzer.analyze(geom)
assert r1 == r2
def test_difficulty_weights_must_sum_to_one():
import pytest
from pydantic import ValidationError
with pytest.raises(ValidationError):
DifficultyWeights(misplacement=0.9, whitespace=0.2, faintness=0.2, smallness=0.15, low_texture=0.10)