tests/test_archtable.py
back to source
import os
import subprocess
import unittest
from numos.archtable import render_arch_table
from numos import seed
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ARCH_TABLE = os.path.join(ROOT, "boot", "lib", "arch_table.sh")
class TestSeed(unittest.TestCase):
def test_nine_arch_targets_seeded(self):
self.assertEqual(len(seed.ARCH_TARGETS), 9)
def test_every_canonical_is_in_its_own_aliases(self):
for target in seed.ARCH_TARGETS:
self.assertIn(target["canonical"], target["aliases"])
def test_aliases_are_globally_unique(self):
seen = []
for target in seed.ARCH_TARGETS:
seen.extend(target["aliases"])
self.assertEqual(len(seen), len(set(seen)))
def test_riscv64_is_present(self):
self.assertIn("riscv64", [t["canonical"] for t in seed.ARCH_TARGETS])
def test_every_target_has_a_busybox_fallback(self):
for target in seed.ARCH_TARGETS:
self.assertTrue(target["busybox_variant"])
class TestRenderArchTable(unittest.TestCase):
def setUp(self):
self.sh = render_arch_table(seed.ARCH_TARGETS)
def test_marked_generated(self):
self.assertIn("GENERATED", self.sh)
self.assertIn("DO NOT EDIT", self.sh)
def test_defines_both_functions(self):
self.assertIn("numos_canonical_arch()", self.sh)
self.assertIn("numos_arch_has_static()", self.sh)
def test_every_alias_of_every_record_has_an_arm(self):
# A single spot-check ("x86_64|amd64)") would pass while any other
# arch's aliases were missing or attached to the wrong canonical.
for target in seed.ARCH_TARGETS:
arm = " %s) echo %s ;;" % ("|".join(target["aliases"]),
target["canonical"])
self.assertIn(arm, self.sh.split("\n"))
def test_unknown_arch_returns_nonzero(self):
self.assertIn("*) return 1 ;;", self.sh)
def test_contains_no_bashisms(self):
for bashism in ("[[", "declare ", "local ", "${!"):
self.assertNotIn(bashism, self.sh)
def test_is_ascii(self):
self.assertTrue(all(ord(c) < 128 for c in self.sh),
"arch_table.sh contains a non-ASCII character")
def test_deterministic(self):
self.assertEqual(self.sh, render_arch_table(seed.ARCH_TARGETS))
class TestCommittedTableIsInSync(unittest.TestCase):
"""boot/lib/arch_table.sh is generated and marked DO NOT EDIT.
It is the one artifact carrying the design's central claim -- that
adding an architecture is a node insert, not a code edit. Edit
numos/seed.py and forget `py -m numos.archtable` and the bootstrap
resolves architectures from stale data with every other test still
green, which would quietly falsify the claim.
"""
def test_committed_file_is_byte_identical_to_its_generator_output(self):
with open(ARCH_TABLE, "r", newline="") as handle:
committed = handle.read()
self.assertEqual(
committed, render_arch_table(seed.ARCH_TARGETS),
"boot/lib/arch_table.sh has drifted from numos/seed.py: "
"regenerate with `py -m numos.archtable`")
def test_committed_file_is_ascii_with_lf_endings(self):
with open(ARCH_TABLE, "rb") as handle:
raw = handle.read()
self.assertNotIn(b"\r", raw)
self.assertTrue(all(byte < 128 for byte in raw))
class TestCommittedTableResolvesEveryAlias(unittest.TestCase):
"""Run the committed table through a real shell, not just grep it.
Every alias of every record is resolved in one bash invocation, so a
wrong arm, a shadowed pattern or a broken `case` shows up as a wrong
answer rather than a passing substring match.
"""
@classmethod
def setUpClass(cls):
aliases = []
for target in seed.ARCH_TARGETS:
aliases.extend(target["aliases"])
script = (
'. "%s"\n'
'for a in %s notanarch; do\n'
' c="$(numos_canonical_arch "$a")" || c=UNRESOLVED\n'
' if numos_arch_has_static "$c"; then s=static; else s=fallback; fi\n'
' echo "$a=$c/$s"\n'
'done\n' % (ARCH_TABLE.replace("\\", "/"), " ".join(aliases)))
proc = subprocess.run(["bash", "-c", script], capture_output=True, text=True)
cls.rc = proc.returncode
cls.answers = dict(
line.split("=", 1) for line in proc.stdout.strip().split("\n") if "=" in line)
cls.stderr = proc.stderr
def test_shell_ran_cleanly(self):
self.assertEqual(self.rc, 0, self.stderr)
def test_every_alias_resolves_to_its_own_canonical(self):
for target in seed.ARCH_TARGETS:
expected = "%s/%s" % (target["canonical"],
"static" if target["zig_triple"] else "fallback")
for alias in target["aliases"]:
self.assertEqual(self.answers.get(alias), expected,
"alias %s resolved wrong" % alias)
def test_an_unlisted_arch_stays_unresolved(self):
self.assertEqual(self.answers.get("notanarch"), "UNRESOLVED/fallback")
if __name__ == "__main__":
unittest.main()