Files
StarPilot/tools/ci/tests/test_qcom_artifact_contract.py
T
firestar5683 8cb60d82fe Developer and build tooling
Provide laptop and device build entry points, cache compatibility checks and developer tooling. Keep distribution assets and model packaging available before runtime consumers.
2026-10-03 22:34:37 +00:00

308 lines
16 KiB
Python

import copy
import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from tools.laptop_device_build import qcom_artifact_contract as contract
ROOT = Path(__file__).resolve().parents[3]
class TestQcomArtifactContract(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory()
self.addCleanup(temporary.cleanup)
self.package = Path(temporary.name)
self.artifacts = self.package / "artifacts"
self.artifacts.mkdir()
self.commands = contract.commands(ROOT)
for name in self.commands:
(self.artifacts / name).write_bytes(f"artifact:{name}".encode())
with patch("tools.laptop_device_build.validate_artifacts.require_qcom_pickle"):
manifest = contract.record(ROOT, self.artifacts)
(self.package / "manifest.json").write_text(json.dumps(manifest))
def verify(self, name):
with patch("tools.laptop_device_build.validate_artifacts.require_qcom_pickle"):
return contract.verify(self.package, ROOT, name, self.commands[name])
def compatible_package(self):
manifest = json.loads((self.package / "manifest.json").read_text())
current = contract.source_inventory(ROOT)
old_hash = "0" * 64
manifest["source"]["sources"][contract.COMPATIBILITY_SOURCE] = old_hash
manifest["source_sha256"] = contract._inventory_signature(manifest["source"])
(self.package / "manifest.json").write_text(json.dumps(manifest))
evidence_dir = self.package / "compatibility"
evidence_dir.mkdir()
(evidence_dir / "qualification.txt").write_text("reviewed paired output evidence\n")
observations = {name: [{"input": label, "shape": contract.COMPATIBILITY_OUTPUTS[name][0],
"dtype": contract.COMPATIBILITY_OUTPUTS[name][1],
"build_sha256": str(index + 4) * 64,
"runtime_sha256": str(index + 4) * 64}
for index, label in enumerate(("A", "B", "A"))]
for name in self.commands}
evidence = {"version": 1, "method": "paired-qcom-output-sha256",
"build_manifest_sha256": contract._sha(self.package / "manifest.json"),
"compatible_source_sha256": contract._inventory_signature(current),
"changed_source": {"path": contract.COMPATIBILITY_SOURCE, "build_sha256": old_hash,
"runtime_sha256": current["sources"][contract.COMPATIBILITY_SOURCE]},
"artifact_sha256": {name: entry["sha256"] for name, entry in manifest["artifacts"].items()},
"paired_outputs": observations,
"evidence_files": {"qualification.txt": contract._sha(evidence_dir / "qualification.txt")}}
(self.package / "compatibility.json").write_text(json.dumps(evidence))
return evidence, current
def test_exact_source_and_artifacts_select_expected_target(self):
name = "driving_tinygrad.pkl"
self.assertEqual(self.verify(name)[0], self.artifacts / name)
self.assertEqual(set(json.loads((self.package / "manifest.json").read_text())["artifacts"]), set(self.commands))
def test_source_and_command_changes_reject(self):
name = "driving_tinygrad.pkl"
with patch.object(contract, "_sources", return_value={"changed.py": "0" * 64}), \
self.assertRaisesRegex(ValueError, "sources changed"):
self.verify(name)
with self.assertRaisesRegex(ValueError, "command recipe changed"):
contract.verify(self.package, ROOT, name, self.commands[name] + " --different")
def test_one_artifact_change_or_missing_member_rejects_whole_package(self):
(self.artifacts / "dm_warp_1344x760_tinygrad.pkl").write_bytes(b"changed")
with self.assertRaisesRegex(ValueError, "artifact mismatch"):
self.verify("driving_tinygrad.pkl")
manifest = json.loads((self.package / "manifest.json").read_text())
del manifest["artifacts"]["dm_warp_1344x760_tinygrad.pkl"]
(self.package / "manifest.json").write_text(json.dumps(manifest))
with self.assertRaisesRegex(ValueError, "source or artifact set"):
self.verify("driving_tinygrad.pkl")
def test_wrong_backend_pickle_rejects_even_with_matching_hash(self):
with self.assertRaises(ValueError):
contract.verify(self.package, ROOT, "driving_tinygrad.pkl", self.commands["driving_tinygrad.pkl"])
def test_malformed_manifest_artifacts_reject_cleanly(self):
manifest = json.loads((self.package / "manifest.json").read_text())
for malformed in (None, [], "not-a-map"):
manifest["artifacts"] = malformed
(self.package / "manifest.json").write_text(json.dumps(manifest))
with self.subTest(malformed=malformed), self.assertRaisesRegex(ValueError, "artifact set"):
self.verify("driving_tinygrad.pkl")
def test_changed_bytes_during_copy_leave_existing_target(self):
name = "driving_tinygrad.pkl"
target = self.package / name
target.write_bytes(b"previous")
with patch("tools.laptop_device_build.validate_artifacts.require_qcom_pickle"), \
patch.object(contract.shutil, "copyfileobj", side_effect=lambda source, output: output.write(b"changed")), \
self.assertRaisesRegex(ValueError, "changed during copy"):
contract.import_artifact(self.package, ROOT, target, self.commands[name])
self.assertEqual(target.read_bytes(), b"previous")
self.assertFalse(list(self.package.glob(".qcom-import-*")))
def test_exact_compatible_source_and_all_six_artifacts_verify(self):
self.compatible_package()
for name in self.commands:
with self.subTest(name=name):
self.assertEqual(self.verify(name)[0], self.artifacts / name)
def test_compatible_source_rejects_unrelated_source_or_recipe_change(self):
_, current = self.compatible_package()
changed = copy.deepcopy(current)
changed["sources"]["unrelated.py"] = "1" * 64
with patch.object(contract, "source_inventory", return_value=changed), \
self.assertRaisesRegex(ValueError, "outside reviewed"):
self.verify("driving_tinygrad.pkl")
changed = copy.deepcopy(current)
changed["commands"]["driving_tinygrad.pkl"].append("--new-option")
with patch.object(contract, "source_inventory", return_value=changed), \
self.assertRaisesRegex(ValueError, "outside reviewed"):
self.verify("driving_tinygrad.pkl")
def test_compatible_source_rejects_artifact_or_paired_output_change(self):
evidence, _ = self.compatible_package()
del evidence["paired_outputs"]["dmonitoring_model_tinygrad.pkl"]
(self.package / "compatibility.json").write_text(json.dumps(evidence))
with self.assertRaisesRegex(ValueError, "lacks a stock target"):
self.verify("driving_tinygrad.pkl")
evidence["paired_outputs"]["dmonitoring_model_tinygrad.pkl"] = [
{"input": label, "shape": contract.COMPATIBILITY_OUTPUTS["dmonitoring_model_tinygrad.pkl"][0],
"dtype": "float32", "build_sha256": str(index + 4) * 64,
"runtime_sha256": str(index + 4) * 64}
for index, label in enumerate(("A", "B", "A"))]
(self.package / "compatibility.json").write_text(json.dumps(evidence))
(self.artifacts / "dmonitoring_model_tinygrad.pkl").write_bytes(b"changed")
with self.assertRaisesRegex(ValueError, "artifact mismatch"):
self.verify("driving_tinygrad.pkl")
def test_compatible_source_rejects_changed_or_unsafe_evidence(self):
evidence, _ = self.compatible_package()
evidence_file = self.package / "compatibility" / "qualification.txt"
evidence_file.write_text("changed evidence\n")
with self.assertRaisesRegex(ValueError, "evidence file mismatch"):
self.verify("driving_tinygrad.pkl")
evidence_file.write_text("reviewed paired output evidence\n")
evidence["evidence_files"] = {"../qualification.txt": contract._sha(evidence_file)}
(self.package / "compatibility.json").write_text(json.dumps(evidence))
with self.assertRaisesRegex(ValueError, "evidence filename"):
self.verify("driving_tinygrad.pkl")
evidence["evidence_files"] = {"qualification.txt": contract._sha(evidence_file)}
(self.package / "compatibility.json").write_text(json.dumps(evidence))
outside = self.package / "outside.txt"
outside.write_text("reviewed paired output evidence\n")
evidence_file.unlink()
evidence_file.symlink_to(outside)
with self.assertRaisesRegex(ValueError, "evidence file mismatch"):
self.verify("driving_tinygrad.pkl")
evidence_file.unlink()
evidence_file.write_text("reviewed paired output evidence\n")
evidence_dir = self.package / "compatibility"
evidence_dir.rename(self.package / "actual_evidence")
evidence_dir.symlink_to(self.package / "actual_evidence", target_is_directory=True)
with self.assertRaisesRegex(ValueError, "evidence directory"):
self.verify("driving_tinygrad.pkl")
def test_compatible_source_rejects_unequal_outputs_and_stale_source_binding(self):
evidence, _ = self.compatible_package()
original = copy.deepcopy(evidence)
evidence["paired_outputs"]["driving_tinygrad.pkl"][1]["runtime_sha256"] = "a" * 64
(self.package / "compatibility.json").write_text(json.dumps(evidence))
with self.assertRaisesRegex(ValueError, "Invalid QCOM paired outputs"):
self.verify("driving_tinygrad.pkl")
original["compatible_source_sha256"] = "b" * 64
(self.package / "compatibility.json").write_text(json.dumps(original))
with self.assertRaisesRegex(ValueError, "not bound"):
self.verify("driving_tinygrad.pkl")
def test_compatible_source_rejects_bool_versions_and_duplicate_json_fields(self):
evidence, _ = self.compatible_package()
evidence["version"] = True
(self.package / "compatibility.json").write_text(json.dumps(evidence))
with self.assertRaisesRegex(ValueError, "not bound"):
self.verify("driving_tinygrad.pkl")
(self.package / "compatibility.json").write_text('{"version":1,"version":1}')
with self.assertRaisesRegex(ValueError, "Duplicate QCOM manifest field"):
self.verify("driving_tinygrad.pkl")
manifest = json.loads((self.package / "manifest.json").read_text())
manifest["version"] = True
(self.package / "manifest.json").write_text(json.dumps(manifest))
with self.assertRaisesRegex(ValueError, "source or artifact set"):
self.verify("driving_tinygrad.pkl")
class TestQcomSupplementalRuntimeCompatibility(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory()
self.addCleanup(temporary.cleanup)
self.package = Path(temporary.name)
self.current = {"sources": {name: hashes[1] for name, hashes in contract.RUNTIME_EXTENSION_CHANGES.items()},
"commands": {"driving_tinygrad.pkl": ["DEV=QCOM", "IMAGE=1", "compile"]}}
self.current["sources"].update(contract.RUNTIME_EXTENSION_ADDITIONS)
self.current["sources"].update({contract.COMPATIBILITY_SOURCE: "a" * 64, "model.onnx": "b" * 64})
parent = copy.deepcopy(self.current)
for name, hashes in contract.RUNTIME_EXTENSION_CHANGES.items():
parent["sources"][name] = hashes[0]
for name in contract.RUNTIME_EXTENSION_ADDITIONS:
del parent["sources"][name]
saved = copy.deepcopy(parent)
saved["sources"][contract.COMPATIBILITY_SOURCE] = "c" * 64
self.manifest = {"source": saved, "source_sha256": contract._inventory_signature(saved),
"artifacts": {name: {"sha256": "d" * 64} for name in contract.COMPATIBILITY_OUTPUTS}}
self.manifest_path = self.package / "manifest.json"
self.manifest_path.write_text(json.dumps(self.manifest))
evidence_dir = self.package / "compatibility"
evidence_dir.mkdir()
(evidence_dir / "outputs.txt").write_text("A/B/A output hashes\n")
self.evidence = {"version": 1, "method": "paired-qcom-output-sha256",
"build_manifest_sha256": contract._sha(self.manifest_path),
"compatible_source_sha256": contract._inventory_signature(parent),
"changed_source": {"path": contract.COMPATIBILITY_SOURCE, "build_sha256": "c" * 64, "runtime_sha256": "a" * 64},
"artifact_sha256": {name: "d" * 64 for name in contract.COMPATIBILITY_OUTPUTS},
"paired_outputs": {name: [{"input": label, "shape": shape, "dtype": dtype,
"build_sha256": "e" * 64, "runtime_sha256": "e" * 64}
for label in ("A", "B", "A")]
for name, (shape, dtype) in contract.COMPATIBILITY_OUTPUTS.items()},
"evidence_files": {"outputs.txt": contract._sha(evidence_dir / "outputs.txt")}}
self.parent_path = self.package / "compatibility.json"
self.parent_path.write_text(json.dumps(self.evidence))
self.bind_extension()
def bind_extension(self):
extension = {"version": 1, "profile": contract.RUNTIME_EXTENSION_PROFILE,
"parent_evidence_sha256": contract._sha(self.parent_path),
"compatible_source_sha256": contract._inventory_signature(self.current)}
(self.package / "runtime-compatibility.json").write_text(json.dumps(extension))
def verify(self):
contract._verify_runtime_compatibility(self.package, self.manifest_path, self.manifest,
self.current, dict.fromkeys(contract.COMPATIBILITY_OUTPUTS))
def test_reviewed_extension_preserves_original_manifest_and_parent_attestation(self):
before = self.manifest_path.read_bytes(), self.parent_path.read_bytes()
self.verify()
self.assertEqual(before, (self.manifest_path.read_bytes(), self.parent_path.read_bytes()))
def test_each_reviewed_source_and_added_helper_requires_exact_bytes(self):
original = copy.deepcopy(self.current)
for name in (*contract.RUNTIME_EXTENSION_CHANGES, *contract.RUNTIME_EXTENSION_ADDITIONS):
with self.subTest(name=name):
self.current = copy.deepcopy(original)
self.current["sources"][name] = "f" * 64
self.bind_extension()
with self.assertRaisesRegex(ValueError, "Unreviewed QCOM runtime"):
self.verify()
def test_extra_missing_or_model_changed_source_is_not_normalized_away(self):
original = copy.deepcopy(self.current)
for kind in ("extra", "missing", "model"):
with self.subTest(kind=kind):
self.current = copy.deepcopy(original)
if kind == "extra": self.current["sources"]["unexpected.py"] = "f" * 64
elif kind == "missing": del self.current["sources"]["model.onnx"]
else: self.current["sources"]["model.onnx"] = "f" * 64
self.bind_extension()
with self.assertRaisesRegex(ValueError, "outside reviewed"):
self.verify()
def test_compile_command_change_remains_rejected(self):
self.current["commands"]["driving_tinygrad.pkl"].append("FLOAT16=0")
self.bind_extension()
with self.assertRaisesRegex(ValueError, "outside reviewed"):
self.verify()
def test_parent_evidence_mutation_and_missing_supplement_reject(self):
original = self.parent_path.read_bytes()
self.parent_path.write_bytes(original + b"\n")
with self.assertRaisesRegex(ValueError, "parent evidence"):
self.verify()
self.parent_path.write_bytes(original)
(self.package / "runtime-compatibility.json").unlink()
with self.assertRaisesRegex(ValueError, "outside reviewed"):
self.verify()
def test_artifact_substitution_and_unequal_paired_output_reject_after_rebinding(self):
original = copy.deepcopy(self.evidence)
for kind in ("artifact", "output"):
with self.subTest(kind=kind):
self.evidence = copy.deepcopy(original)
if kind == "artifact": self.evidence["artifact_sha256"]["driving_tinygrad.pkl"] = "f" * 64
else: self.evidence["paired_outputs"]["driving_tinygrad.pkl"][0]["runtime_sha256"] = "f" * 64
self.parent_path.write_text(json.dumps(self.evidence))
self.bind_extension()
with self.assertRaises(ValueError):
self.verify()
def test_supplement_symlink_unknown_profile_and_extra_fields_reject(self):
path = self.package / "runtime-compatibility.json"
good = json.loads(path.read_text())
for mutation in ({"profile": "unreviewed"}, {"extra": True}, {"version": True}):
path.write_text(json.dumps(good | mutation))
with self.subTest(mutation=mutation), self.assertRaises(ValueError):
self.verify()
path.unlink()
path.symlink_to(self.parent_path)
with self.assertRaisesRegex(ValueError, "Invalid QCOM runtime extension"):
self.verify()