#!/usr/bin/env python3 """Smoke tests for audit-kf6-deps.py. Run with: python3 -m unittest local/scripts/tests/test_audit_kf6_deps.py """ import re import sys import unittest from pathlib import Path SCRIPTS_DIR = Path(__file__).resolve().parent.parent sys.path.insert(0, str(SCRIPTS_DIR)) import importlib.util # noqa: E402 _spec = importlib.util.spec_from_file_location( "akd", SCRIPTS_DIR / "audit-kf6-deps.py" ) assert _spec is not None and _spec.loader is not None akd = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(akd) class TestKF6RegexForms(unittest.TestCase): """The four find_package forms must all be recognized.""" def test_form_1_direct_namespace(self): text = "find_package(KF6::Foo REQUIRED)" self.assertIn("KF6::Foo", _scan(text)) def test_form_2_components_block(self): text = "find_package(KF6 6.26.0 REQUIRED COMPONENTS Foo Bar)" found = _scan(text) self.assertIn("KF6::Foo", found) self.assertIn("KF6::Bar", found) def test_form_3_dominant_named(self): text = "find_package(KF6KDED ${VER} REQUIRED)" self.assertIn("KF6::KDED", _scan(text)) def test_form_4_plain_name(self): text = "find_package(KF6 XmlRpc REQUIRED)" self.assertIn("KF6::XmlRpc", _scan(text)) def test_self_reference_kf6kf6_filtered(self): text = "find_package(KF6KF6Foo REQUIRED)" # KF6KF6Foo slices to KF6Foo — the "Foo" remains valid. # But "find_package(KF6KF6 ${VAR})" is filtered. text2 = "NAMESPACE KF6::" # Direct regex matches "KF6::" (no Foo); NAMED doesn't match. # Just confirm no crash + empty-ish result. self.assertIsInstance(_scan(text2), set) def test_qt6_modules(self): text = "find_package(Qt6Core REQUIRED) find_package(Qt6Qml QUIET)" # Qt6Core -> qtbase, Qt6Qml -> qtdeclarative qt6 = _scan_qt6(text) self.assertIn("Qt6Core", qt6) self.assertIn("Qt6Qml", qt6) deps = {akd.normalize_dep_name(c) for c in qt6} self.assertIn("qtbase", deps) self.assertIn("qtdeclarative", deps) class TestNormalizeDepName(unittest.TestCase): def test_kf6_kio(self): self.assertEqual(akd.normalize_dep_name("KF6::KIO"), "kf6-kio") def test_kf6_kcmutils_override(self): self.assertEqual(akd.normalize_dep_name("KF6::KCMUtils"), "kf6-kcmutils") def test_kf6_kded6_override(self): self.assertEqual(akd.normalize_dep_name("KF6::KDED"), "kf6-kded6") def test_qt6guifrivate_qtbase(self): self.assertEqual(akd.normalize_dep_name("Qt6GuiPrivate"), "qtbase") def test_qt6concurrent_qtbase(self): self.assertEqual(akd.normalize_dep_name("Qt6Concurrent"), "qtbase") class TestDiscoverSkipsWIP(unittest.TestCase): def test_wip_path_excluded(self): """Per local/AGENTS.md local-over-WIP policy: WIP paths skipped.""" # We can't easily test discover_kf6_recipes without filesystem state, # but we can inspect the function source for the wip-skip clause. import inspect src = inspect.getsource(akd.discover_kf6_recipes) self.assertIn('"wip"', src) self.assertIn("if \"wip\" in recipe_toml.parts", src) class TestNoFetchHonesty(unittest.TestCase): """--no-fetch must produce exit 2 (not 0) when every entry is skipped.""" def test_no_fetch_json_exits_2(self): import subprocess rc = subprocess.run( ["python3", str(SCRIPTS_DIR / "audit-kf6-deps.py"), "--no-fetch", "--json"], capture_output=True, text=True, ).returncode self.assertEqual(rc, 2, f"expected 2 (all-skipped), got {rc}; stdout: " f"{subprocess.run.__name__}") def _scan(text): """Run scan_source logic on a synthetic text blob (KF6 only).""" kf6 = set() for m in akd.KF6_DIRECT_RE.finditer(text): kf6.add(m.group(1)) for m in akd.KF6_COMPONENTS_BLOCK_RE.finditer(text): for tok in akd.KF6_COMPONENT_TOKEN_RE.findall(m.group(0)): if tok in ("REQUIRED", "QUIET", "COMPONENTS", "CONFIG", "VERSION", "EXACT", "QUIETLY", "MODULE", "KF6"): continue kf6.add(f"KF6::{tok}") for m in akd.KF6_NAMED_RE.finditer(text): rest = m.group(1)[len("KF6"):] if rest.startswith("KF6") or not rest: continue kf6.add(f"KF6::{rest}") for m in akd.KF6_PLAIN_NAME_RE.finditer(text): kf6.add(f"KF6::{m.group(1)}") return kf6 def _scan_qt6(text): """Run scan_source Qt6 logic on a synthetic text blob.""" qt6 = set() for m in akd.QT6_COMPONENT_RE.finditer(text): qt6.add(f"Qt6{m.group(1)}") if akd.QT6_GENERIC_RE.search(text): qt6.add("Qt6Core") return qt6 if __name__ == "__main__": unittest.main()