Files
unredact/tests/test_solver.py
T
2026-02-15 22:07:41 -06:00

122 lines
3.5 KiB
Python

import pytest
from PIL import ImageFont
from unredact.pipeline.solver import solve_gap, SolveResult
def _get_test_font() -> ImageFont.FreeTypeFont:
import subprocess
result = subprocess.run(
["fc-match", "--format=%{file}", "Liberation Serif"],
capture_output=True, text=True,
)
path = result.stdout.strip()
return ImageFont.truetype(path, 40)
class TestSolveGap:
def test_finds_exact_match(self):
"""Given a gap that exactly fits 'hello', solver should find it."""
font = _get_test_font()
target = font.getlength("hello")
results = solve_gap(
font=font,
charset="ehlo",
target_width=target,
tolerance=0.5,
min_length=5,
max_length=5,
left_context="",
right_context="",
)
texts = [r.text for r in results]
assert "hello" in texts
def test_respects_tolerance_zero(self):
"""With zero tolerance, only exact pixel matches are returned."""
font = _get_test_font()
target = font.getlength("ab")
results = solve_gap(
font=font,
charset="ab",
target_width=target,
tolerance=0.0,
min_length=2,
max_length=2,
left_context="",
right_context="",
)
texts = [r.text for r in results]
assert "ab" in texts
def test_respects_length_bounds(self):
"""Results should all be within min/max length."""
font = _get_test_font()
target = font.getlength("test")
results = solve_gap(
font=font,
charset="tes",
target_width=target,
tolerance=2.0,
min_length=3,
max_length=5,
left_context="",
right_context="",
)
for r in results:
assert 3 <= len(r.text) <= 5
def test_with_context_chars(self):
"""Solver accounts for left/right boundary kerning."""
font = _get_test_font()
# Measure "es" when preceded by "T"
target = font.getlength("Tes") - font.getlength("T")
results = solve_gap(
font=font,
charset="est",
target_width=target,
tolerance=0.5,
min_length=2,
max_length=2,
left_context="T",
right_context="",
)
texts = [r.text for r in results]
assert "es" in texts
def test_empty_results_for_impossible_width(self):
"""If target is impossibly small, return no results."""
font = _get_test_font()
results = solve_gap(
font=font,
charset="abc",
target_width=0.1,
tolerance=0.0,
min_length=1,
max_length=5,
left_context="",
right_context="",
)
assert len(results) == 0
def test_result_contains_width_and_error(self):
"""Each result should have text, width, and error fields."""
font = _get_test_font()
target = font.getlength("ab")
results = solve_gap(
font=font,
charset="ab",
target_width=target,
tolerance=1.0,
min_length=2,
max_length=2,
left_context="",
right_context="",
)
assert len(results) > 0
r = results[0]
assert isinstance(r.text, str)
assert isinstance(r.width, float)
assert isinstance(r.error, float)
assert r.error >= 0