aboutsummaryrefslogtreecommitdiff
path: root/tools/tests/test_lint_project.py
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-18 21:10:37 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-18 21:10:37 +0200
commit2292bf700d28ade39cf2d64b45509e6db21034bc (patch)
treeef455320d39ebe51db236219dbbda2b148dcbe18 /tools/tests/test_lint_project.py
parent85f1ecabd7f58f306b2a042376209ac8cf49ab9b (diff)
test(tools): add screenshot, budget and product checks; enforce file headers
- sim_screenshot.py finds the device drawing in the simulator window, crops the screen and compares it with approved golden images. - check_budgets.py reads key=value DIAG/HARNESS lines (the simulator console escapes quotes, so JSON was dropped). - ciq_test_report.py accepts SDK 9.2.0's real summary line `PASSED (passed=N, failed=N, errors=N)`; a captured log is a fixture. - gen_products.py checks all manifest products are watches with API 5.0+ and at least 512 KB for apps. - lint_project.py rule HDR001: every file carries the Author/Summary header (JSON keys, PNG text chunks; generated package-lock exempt). - make_icon.py --all draws one icon per launcher size in use. - Golden screenshots for fr255s, venusq2, fr70 and fr965. - 44 tests, warnings as errors, ruff and mypy --strict clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tools/tests/test_lint_project.py')
-rw-r--r--tools/tests/test_lint_project.py43
1 files changed, 42 insertions, 1 deletions
diff --git a/tools/tests/test_lint_project.py b/tools/tests/test_lint_project.py
index be0151e..31f8125 100644
--- a/tools/tests/test_lint_project.py
+++ b/tools/tests/test_lint_project.py
@@ -1,11 +1,16 @@
+# Author: cirakg & claude ai
+# Summary: Tests for tools/lint_project.py, the project policy linter.
"""Tests for tools/lint_project.py, the project policy linter."""
from __future__ import annotations
+import json
from pathlib import Path
import lint_project as lp
from conftest import PROJECT_ROOT
+from PIL import Image
+from PIL.PngImagePlugin import PngInfo
GOOD_MANIFEST = "<iq:manifest><iq:permissions/></iq:manifest>\n"
@@ -19,7 +24,8 @@ def make_project(tmp_path: Path, files: dict[str, str]) -> Path:
def codes(root: Path) -> list[tuple[str, str, int]]:
- return [(str(p.path), p.code, p.line) for p in lp.lint(root)]
+ """Findings of the code rules (MC001-MC005) only."""
+ return [(str(p.path), p.code, p.line) for p in sorted(lp.lint_code(root))]
def test_clean_project_passes(tmp_path: Path) -> None:
@@ -76,6 +82,41 @@ def test_manifest_permissions_are_rejected(tmp_path: Path) -> None:
assert codes(root) == [("manifest.xml", "MC005", 2)]
+HEADER = "# Author: cirakg & claude ai\n# Summary: A file with a proper header.\n"
+
+
+def header_findings(root: Path) -> list[str]:
+ return sorted(str(p.path) for p in lp.lint_headers(root))
+
+
+def save_png(path: Path, author: str | None) -> None:
+ info = PngInfo()
+ if author is not None:
+ info.add_text("Author", author)
+ info.add_text("Description", "test image")
+ path.parent.mkdir(parents=True, exist_ok=True)
+ Image.new("RGB", (4, 4)).save(path, pnginfo=info)
+
+
+def test_headers_are_required_in_every_file_type(tmp_path: Path) -> None:
+ files = {
+ "ok.sh": "#!/bin/sh\n" + HEADER,
+ "late.txt": "\n" * 20 + HEADER, # too far down
+ "none.md": "# Title\n",
+ "data/ok.json": json.dumps({"_author": lp.AUTHOR, "_summary": "x"}),
+ "data/bad.json": json.dumps({"author": "someone else", "description": "x"}),
+ "docker/docs/package-lock.json": "{}", # generated, exempt
+ "build/out.txt": "ignored folder",
+ }
+ for name, text in files.items():
+ path = tmp_path / name
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_text(text, encoding="utf-8")
+ save_png(tmp_path / "img/ok.png", lp.AUTHOR)
+ save_png(tmp_path / "img/anon.png", None)
+ assert header_findings(tmp_path) == ["data/bad.json", "img/anon.png", "late.txt", "none.md"]
+
+
def test_project_passes_its_own_rules() -> None:
problems = lp.lint(PROJECT_ROOT)
assert problems == [], "\n".join(map(str, problems))