Files
firmware-lowlevel-development/skills/firmware-lowlevel-psp-development/scripts/psp/test_validate_bsp_handoff.py
T

226 lines
9.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Behavioral tests for validate_bsp_handoff.py."""
import hashlib
import json
import os
import tempfile
from pathlib import Path
from unittest.mock import patch
import validate_bsp_handoff
def main() -> int:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
output_root = root / "outputs"
imgs = output_root / "CV1841CP-BOARD_dualos" / "imgs" / "20260902_1"
imgs.mkdir(parents=True)
artifact = imgs / "boot.bin"
artifact.write_bytes(b"boot")
kernel = imgs / "kernel.bin"
kernel.write_bytes(b"kernel")
rootfs = imgs / "rootfs.bin"
rootfs.write_bytes(b"rootfs")
project = root / "CV1841CP-BOARD_dualos"
partition = project / "configs" / "board" / "partition" / "partition_spinand.xml"
partition.parent.mkdir(parents=True)
partition.write_text(
"""<physical_partition type="spinand">
<partition label="BOOT" alias="kernel.A" size_in_kb="1024" file="kernel.bin"/>
<partition label="ROOTFS" alias="rootfs.A" size_in_kb="1024" file="rootfs.bin"/>
</physical_partition>
""",
encoding="utf-8",
)
checks = []
for image in (kernel, rootfs):
size = image.stat().st_size
checks.append(
{
"image": image.name,
"image_path": str(image),
"image_size_bytes": size,
"partitions": [
{
"label": "BOOT" if image.name == "kernel.bin" else "ROOTFS",
"alias": "kernel.A" if image.name == "kernel.bin" else "rootfs.A",
"file": image.name,
"size_in_kb": 1024,
"size_bytes": 1024 * 1024,
}
],
"effective_partition_size_bytes": 1024 * 1024,
"required_margin_bytes": 512 * 1024,
"actual_margin_bytes": 1024 * 1024 - size,
"passed": True,
}
)
margin = {
"schema_version": 1,
"passed": True,
"checked_at": "2026-09-02T00:00:00+00:00",
"partition_table": str(partition),
"partition_table_sha256": hashlib.sha256(partition.read_bytes()).hexdigest(),
"required_margin_bytes": 512 * 1024,
"checks": checks,
}
margin_path = project / "doc" / "bsp_adaptation" / "validation" / "image_partition_margin_20260902_1.json"
margin_path.parent.mkdir(parents=True)
margin_path.write_text(json.dumps(margin), encoding="utf-8")
permissions = {
"schema_version": 1,
"passed": True,
"policy": "a+rX",
"output_root": str(output_root),
"imgs_version_path": str(imgs),
}
permissions_path = margin_path.parent / "output_permissions_20260902_1.json"
permissions_path.write_text(json.dumps(permissions), encoding="utf-8")
manifest = root / "psp_handoff.json"
payload = {
"schema_version": 1,
"handoff_type": "bsp-to-psp",
"producer": {
"project": "CV1841CP-BOARD_dualos",
"project_path": str(project),
},
"baseline": {
"chip": "CV1841CP",
"sdk": "cv184x_v6.3.3",
"board_build_type": "cv1841cp_wevb_0015a_spinand",
},
"build": {
"version": "20260902_1",
"status": "build_passed",
"exit_code": 0,
"completion_marker_seen": True,
"imgs_version_path": str(imgs),
"image_partition_margin_path": str(margin_path),
"output_permissions_path": str(permissions_path),
},
"artifacts": [
{
"relative_path": item.name,
"absolute_path": str(item),
"size": item.stat().st_size,
"sha256": hashlib.sha256(item.read_bytes()).hexdigest(),
}
for item in (artifact, kernel, rootfs)
],
"validation": {
"image_partition_margin": margin,
"output_permissions": permissions,
"hardware": "pending",
"owner_acceptance": "pending",
"release": "not-authorized",
},
}
manifest.write_text(json.dumps(payload), encoding="utf-8")
for directory in (output_root, output_root / "CV1841CP-BOARD_dualos", imgs.parent, imgs):
os.chmod(directory, 0o755)
for image in (artifact, kernel, rootfs):
os.chmod(image, 0o644)
result = validate_bsp_handoff.validate_handoff(
manifest,
"CV1841CP-BOARD_dualos",
"CV1841CP",
"cv184x_v6.3.3",
"cv1841cp_wevb_0015a_spinand",
)
assert result["passed"] is True, result
assert result["dependency"]["verified_artifact_count"] == 3
assert result["dependency"]["build_id"] == "20260902_1"
assert result["dependency"]["chip"] == "CV1841CP"
assert result["dependency"]["image_partition_margin"]["passed"] is True
assert result["dependency"]["output_permissions"]["current_check"]["passed"] is True
assert result["dependency"]["artifacts"][0]["sha256"]
assert result["warnings"]
os.chmod(output_root, 0o700)
os.chmod(imgs, 0o700)
os.chmod(kernel, 0o600)
other_build = root / "another-build"
other_build.mkdir(mode=0o700)
(imgs / "external-link").symlink_to(other_build, target_is_directory=True)
readonly = validate_bsp_handoff.validate_handoff(manifest)
assert readonly["passed"] is False
assert any("共享读取/进入权限" in error for error in readonly["errors"])
assert output_root.stat().st_mode & 0o777 == 0o700
repaired = validate_bsp_handoff.validate_handoff(
manifest,
expected_project="CV1841CP-BOARD_dualos",
repair_output_permissions=True,
)
assert repaired["passed"] is True, repaired
assert repaired["dependency"]["output_permissions"]["current_check"]["repaired"] is True
assert repaired["dependency"]["output_permissions"]["current_check"]["symlinks_skipped"] == 1
assert output_root.stat().st_mode & 0o555 == 0o555
assert other_build.stat().st_mode & 0o777 == 0o700
assert kernel.stat().st_mode & 0o444 == 0o444
assert kernel.stat().st_mode & 0o111 == 0
no_evidence_payload = json.loads(json.dumps(payload))
no_evidence_payload["validation"].pop("output_permissions")
no_evidence_manifest = root / "no_permissions_handoff.json"
no_evidence_manifest.write_text(json.dumps(no_evidence_payload), encoding="utf-8")
no_evidence = validate_bsp_handoff.validate_handoff(
no_evidence_manifest,
expected_project="CV1841CP-BOARD_dualos",
repair_output_permissions=True,
)
assert no_evidence["passed"] is False
assert any("权限证据" in error for error in no_evidence["errors"])
os.chmod(kernel, 0o600)
bad_project = validate_bsp_handoff.validate_handoff(
manifest,
expected_project="NOT-THE-PROJECT",
repair_output_permissions=True,
)
assert bad_project["passed"] is False
assert kernel.stat().st_mode & 0o444 != 0o444
bad_evidence_payload = json.loads(json.dumps(payload))
bad_evidence_payload["validation"]["output_permissions"]["output_root"] = str(root / "wrong-root")
bad_evidence_manifest = root / "bad_permissions_handoff.json"
bad_evidence_manifest.write_text(json.dumps(bad_evidence_payload), encoding="utf-8")
bad_evidence = validate_bsp_handoff.validate_handoff(
bad_evidence_manifest,
expected_project="CV1841CP-BOARD_dualos",
repair_output_permissions=True,
)
assert bad_evidence["passed"] is False
assert kernel.stat().st_mode & 0o444 != 0o444
with patch.object(validate_bsp_handoff.os, "chmod", side_effect=PermissionError("denied")):
unrepairable = validate_bsp_handoff.validate_handoff(
manifest,
expected_project="CV1841CP-BOARD_dualos",
repair_output_permissions=True,
)
assert unrepairable["passed"] is False
assert any("无法检查或修复" in error for error in unrepairable["errors"])
os.chmod(kernel, 0o644)
missing_margin_payload = json.loads(json.dumps(payload))
missing_margin_payload["validation"].pop("image_partition_margin")
missing_margin_payload["build"].pop("image_partition_margin_path")
missing_margin_manifest = root / "missing_margin_handoff.json"
missing_margin_manifest.write_text(
json.dumps(missing_margin_payload), encoding="utf-8"
)
missing_margin = validate_bsp_handoff.validate_handoff(missing_margin_manifest)
assert missing_margin["passed"] is False
assert any("分区余量证据" in error for error in missing_margin["errors"])
artifact.write_bytes(b"tampered")
tampered = validate_bsp_handoff.validate_handoff(manifest)
assert tampered["passed"] is False
assert any("大小不匹配" in error for error in tampered["errors"])
print("BSP handoff consumer tests passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())