ffbe098ef8
TLC (Twilight Commander) was missing from both ISO configs. Added
tlc = {} to [packages] in redbear-mini.toml and redbear-full.toml.
Created missing symlink: recipes/tui/tlc -> ../../local/recipes/tui/tlc.
104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Smoke tests for audit-patch-idempotency.py.
|
|
|
|
Run with:
|
|
python3 -m unittest local/scripts/tests/test_audit_patch_idempotency.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(
|
|
"api", SCRIPTS_DIR / "audit-patch-idempotency.py"
|
|
)
|
|
assert _spec is not None and _spec.loader is not None
|
|
api = importlib.util.module_from_spec(_spec)
|
|
_spec.loader.exec_module(api)
|
|
|
|
|
|
class TestCollectPatches(unittest.TestCase):
|
|
"""The patch collector walks local/patches/<component>/NN-*.patch."""
|
|
|
|
def test_collect_real_patches(self):
|
|
# On the live tree, this should find at least 10 patches.
|
|
patches = list(api.collect_patches())
|
|
self.assertGreater(len(patches), 0)
|
|
# Every patch is a 2-tuple (component, Path).
|
|
for comp, p in patches:
|
|
self.assertIsInstance(comp, str)
|
|
self.assertTrue(p.exists())
|
|
|
|
def test_collect_filter_by_component(self):
|
|
# Should find the 3 libdrm patches.
|
|
patches = list(api.collect_patches(component_filter="libdrm"))
|
|
for _, name in patches:
|
|
self.assertIn("libdrm", str(name))
|
|
|
|
def test_collect_nonexistent_component(self):
|
|
patches = list(api.collect_patches(component_filter="does-not-exist-xyz"))
|
|
self.assertEqual(patches, [])
|
|
|
|
|
|
class TestPatchNameValidation(unittest.TestCase):
|
|
"""The regex accepts files matching NN-name.patch."""
|
|
|
|
def test_valid_patch_names(self):
|
|
# The collector uses PATCH_NAME_RE — verify it accepts real names.
|
|
names = [
|
|
"01-foo.patch", "02-bar.patch", "99-trailing-numbers.patch",
|
|
"10-multi-word-name-with-dashes.patch",
|
|
]
|
|
for n in names:
|
|
self.assertTrue(api.PATCH_NAME_RE.match(n),
|
|
f"should accept {n!r}")
|
|
|
|
def test_invalid_patch_names(self):
|
|
for n in ["foo.patch", "01-foo", "01-.patch", "foo-01-bar.patch"]:
|
|
self.assertFalse(api.PATCH_NAME_RE.match(n),
|
|
f"should reject {n!r}")
|
|
|
|
|
|
class TestJSONSchemaHonesty(unittest.TestCase):
|
|
"""--no-fetch must produce JSON with skipped entries and a clear message."""
|
|
|
|
def test_no_fetch_json_shape(self):
|
|
import json
|
|
import subprocess
|
|
proc = subprocess.run(
|
|
["python3", str(SCRIPTS_DIR / "audit-patch-idempotency.py"),
|
|
"--no-fetch", "--json"],
|
|
capture_output=True, text=True,
|
|
)
|
|
# With --no-fetch, every entry is skipped -> exit 2 (CI-safe).
|
|
self.assertEqual(proc.returncode, 2)
|
|
data = json.loads(proc.stdout)
|
|
self.assertIn("patches", data)
|
|
self.assertIn("total", data)
|
|
self.assertIn("errors", data)
|
|
self.assertIn("skipped", data)
|
|
# Every entry must be status=skipped.
|
|
for entry in data["patches"]:
|
|
self.assertEqual(entry["status"], "skipped")
|
|
self.assertEqual(data["skipped"], data["total"])
|
|
|
|
def test_no_fetch_text_honest_about_skipping(self):
|
|
import subprocess
|
|
proc = subprocess.run(
|
|
["python3", str(SCRIPTS_DIR / "audit-patch-idempotency.py"),
|
|
"--no-fetch"],
|
|
capture_output=True, text=True,
|
|
)
|
|
# Must NOT say "All N patches are idempotent" when none were
|
|
# actually audited.
|
|
self.assertIn("SKIPPED", proc.stdout)
|
|
self.assertIn("No audit was performed", proc.stdout)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|