75 lines
3.2 KiB
Python
75 lines
3.2 KiB
Python
"""Host lifecycle tests: dirty dependencies, reproducible stamps, file integrity.
|
|
|
|
Run: python -m unittest discover -s c/firmware-info/tests -p test_identity.py
|
|
"""
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
TOOL = Path(__file__).resolve().parents[1] / "tools/identity.py"
|
|
spec = importlib.util.spec_from_file_location("identity", TOOL)
|
|
identity = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(identity)
|
|
|
|
|
|
class IdentityTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.root = Path(self.temp.name)
|
|
self.config = self.root / "config.json"
|
|
self.config.write_text(json.dumps({"product":"test", "target":"host", "version":[1,2,3]}))
|
|
|
|
def prepare(self, states):
|
|
with patch.object(identity, "git_state", side_effect=states), patch.dict(os.environ, {"SOURCE_DATE_EPOCH":"1791194400"}):
|
|
return identity.prepare(self.root, self.root, self.config, self.root)
|
|
|
|
def test_clean_and_dirty_dependency(self):
|
|
clean = {"commit":"abcdef0123456789", "dirty":False}
|
|
meta = self.prepare([clean,clean])
|
|
self.assertEqual(meta["build_id"], "abcdef01")
|
|
header = (self.root / "firmware_identity.h").read_text()
|
|
self.assertIn("#define FIRMWARE_VERSION_PATCH 3U",header)
|
|
dirty = dict(clean, dirty=True)
|
|
self.assertEqual(self.prepare([clean,dirty])["build_id"], "abcdef0+")
|
|
self.assertEqual(self.prepare([dirty,clean])["build_id"], "abcdef0+")
|
|
|
|
def test_missing_git_is_explicit(self):
|
|
unknown = {"commit":None,"dirty":None}
|
|
self.assertEqual(self.prepare([unknown,unknown])["build_id"], "NOGIT000")
|
|
|
|
def test_invalid_version(self):
|
|
self.config.write_text(json.dumps({"product":"x","target":"x","version":[1,2,1000]}))
|
|
with self.assertRaises(ValueError):
|
|
identity.prepare(self.root,self.root,self.config,self.root)
|
|
|
|
def test_untracked_source_is_dirty(self):
|
|
def git(*args):
|
|
subprocess.run(["git","-C",str(self.root),*args],check=True,capture_output=True)
|
|
git("init")
|
|
git("-c","user.name=Test","-c","user.email=test@example.invalid","commit","--allow-empty","-m","test")
|
|
# config.json is untracked and must prevent a clean-looking build ID.
|
|
self.assertTrue(identity.git_state(self.root)["dirty"])
|
|
|
|
def test_cli_seal_verify_and_corruption(self):
|
|
clean = {"commit":"abcdef0123456789", "dirty":False}
|
|
self.prepare([clean,clean])
|
|
image = self.root / "firmware.bin"
|
|
image.write_bytes(b"abc")
|
|
import sys
|
|
subprocess.run([sys.executable,str(TOOL),"seal","--image",str(image),"--metadata",str(self.root/"firmware_identity.json")],check=True,capture_output=True)
|
|
manifest = image.with_suffix(".bin.manifest.json")
|
|
meta = identity.verify(image,manifest)
|
|
self.assertEqual(meta["sha256"],"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
|
|
image.write_bytes(b"abd")
|
|
with self.assertRaises(ValueError): identity.verify(image,manifest)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|