226 lines
9.6 KiB
Python
Executable File
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())
|