Skip to content
This repository was archived by the owner on Jan 28, 2026. It is now read-only.

Commit dd1495d

Browse files
Start on test suite
1 parent 5b16917 commit dd1495d

9 files changed

Lines changed: 161 additions & 0 deletions

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
dev = [
33
"pre-commit >= 4.5.0"
44
]
5+
tests = [
6+
"pytest >= 9.0.0",
7+
"pytest-cov >= 7.0.0",
8+
"tox >= 4.32.0",
9+
"tox-uv >= 1.29.0"
10+
]
511

612
[project]
713
dependencies = [
@@ -59,6 +65,7 @@ split-on-trailing-comma = false
5965
classmethod-decorators = ["classmethod"]
6066

6167
[tool.ruff.lint.per-file-ignores]
68+
"tests/*" = ["S101"]
6269

6370
[tool.ruff.lint.pydocstyle]
6471
convention = "google"

tests/__init__.py

Whitespace-only changes.

tests/test_extractors.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from pathlib import Path
2+
3+
import calendar_extract as mod
4+
import numpy as np
5+
6+
7+
class FakeTextPage:
8+
def extractWORDS(self, delimiters=None):
9+
return [(0, 0, 10, 10, "January"), (11, 0, 20, 10, "2024")]
10+
11+
12+
class FakePage:
13+
def get_pixmap(self):
14+
class PM:
15+
def tobytes(self, fmt) -> bytes:
16+
return b"\x89PNG\r\n\x1a\n"
17+
18+
return PM()
19+
20+
def get_textpage_ocr(self, tessdata=None):
21+
return FakeTextPage()
22+
23+
24+
class FakeDoc:
25+
def __getitem__(self, idx):
26+
return FakePage()
27+
28+
29+
def test_extract_png_calendars(monkeypatch) -> None:
30+
monkeypatch.setattr(mod.pymupdf, "open", lambda _: FakeDoc())
31+
monkeypatch.setattr(mod.cv2, "imdecode", lambda *_: np.zeros((200, 300, 3)))
32+
33+
calendars = mod.extract_png_calendars(Path("fake.png"))
34+
35+
assert len(calendars) == 1
36+
cal = calendars[0]
37+
assert cal.month == 1
38+
assert cal.year == 2024

tests/test_image_helpers.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import cv2
2+
import numpy as np
3+
from calendar_extract import keep_only_colours, remove_colours
4+
5+
6+
def test_remove_colours_masks_pixels(monkeypatch) -> None:
7+
img = np.zeros((2, 2, 3), dtype=np.uint8)
8+
img[:] = [10, 10, 10]
9+
10+
def fake_in_range(*args, **kwargs):
11+
return np.array([[255, 0], [0, 0]], dtype=np.uint8)
12+
13+
monkeypatch.setattr(cv2, "inRange", fake_in_range)
14+
15+
result = remove_colours(img, [(10, 10, 10)])
16+
17+
assert (result[0, 0] == [255, 255, 255]).all()
18+
19+
20+
def test_keep_only_colours(monkeypatch) -> None:
21+
img = np.ones((2, 2, 3), dtype=np.uint8) * 50
22+
23+
def fake_in_range(*args, **kwargs):
24+
return np.array([[255, 0], [0, 0]], dtype=np.uint8)
25+
26+
monkeypatch.setattr(cv2, "inRange", fake_in_range)
27+
28+
result = keep_only_colours(img, [(50, 50, 50)])
29+
30+
assert result.shape == img.shape

tests/test_main.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import calendar_extract as mod
2+
3+
4+
def test_main_writes_output(monkeypatch, tmp_path) -> None:
5+
monkeypatch.setattr(mod.Path, "glob", lambda self, _: [])
6+
monkeypatch.setattr(mod, "extract_png_calendars", lambda *a, **k: [])
7+
monkeypatch.setattr(mod, "extract_pdf_calendars", lambda *a, **k: [])
8+
9+
monkeypatch.chdir(tmp_path)
10+
11+
mod.main()
12+
13+
output_dir = tmp_path / "output"
14+
assert output_dir.exists()

tests/test_mappings.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from calendar_extract import adjust_mappings
2+
3+
4+
def test_adjust_mappings_single_row() -> None:
5+
mapped = [
6+
{"x0": 0, "y0": 0, "month": 1, "year": 2024},
7+
{"x0": 100, "y0": 0, "month": 2, "year": 2024},
8+
{"x0": 200, "y0": 0, "month": 3, "year": 2024},
9+
]
10+
11+
result = adjust_mappings(
12+
mapped=mapped, edges=(300, 300), x_offset=10, y_offset=10, rows=1, columns=3
13+
)
14+
15+
assert len(result) == 3
16+
assert result[0]["x0"] == 0
17+
assert result[0]["x1"] == 90
18+
assert result[-1]["x1"] == 300

tests/test_models.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from datetime import date
2+
3+
import numpy as np
4+
from calendar_extract import Calendar, Cell
5+
6+
7+
def test_calendar_equality_and_hash() -> None:
8+
img = np.zeros((10, 10, 3))
9+
a = Calendar(month=1, year=2024, calendar_image=img)
10+
b = Calendar(month=1, year=2024, calendar_image=img.copy())
11+
c = Calendar(month=2, year=2024, calendar_image=img)
12+
13+
assert a == b
14+
assert a != c
15+
assert len({a, b, c}) == 2
16+
17+
18+
def test_cell_equality_and_hash() -> None:
19+
d = date(2024, 1, 1)
20+
a = Cell(datestamp=d, colour="red")
21+
b = Cell(datestamp=d, colour="blue")
22+
c = Cell(datestamp=date(2024, 1, 2), colour="red")
23+
24+
assert a == b
25+
assert a != c
26+
assert len({a, b, c}) == 2

tests/test_parse.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from calendar_extract import parse_month
2+
3+
4+
def test_parse_month_valid() -> None:
5+
dt = parse_month("January2024")
6+
assert dt.year == 2024
7+
assert dt.month == 1
8+
9+
10+
def test_parse_month_invalid() -> None:
11+
assert parse_month("FooBar") is None
12+
assert parse_month("") is None

tests/test_process_calendar.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import numpy as np
2+
from calendar_extract import PNG_COLOURS, process_calendar_squares
3+
4+
5+
def test_process_calendar_single_day(monkeypatch) -> None:
6+
img = np.zeros((100, 700, 3), dtype=np.uint8)
7+
8+
def fake_analyze_square(*args, **kwargs):
9+
return (0, 0, 255) # red (BGR)
10+
11+
monkeypatch.setattr("calendar_extract.analyze_square", fake_analyze_square)
12+
13+
cells = process_calendar_squares(img=img, year=2024, month=1, colours=PNG_COLOURS)
14+
15+
assert len(cells) == 31
16+
assert any(cell.is_recycling for cell in cells)

0 commit comments

Comments
 (0)