Files
firmware-lowlevel-development/skills/firmware-lowlevel-bsp-development/scripts/bsp/test_bsp_build_handoff.py
T

188 lines
7.2 KiB
Python

#!/usr/bin/env python3
"""Behavioral tests for bounded preflight and BSP-to-PSP handoff recording."""
import hashlib
import json
import tempfile
from pathlib import Path
import bsp_preflight
import project_guard
import record_build_handoff
def make_project(root: Path, name: str) -> Path:
project = root / name
config = project / "configs" / "cv1841cp_wevb_0015a_spinand"
config.mkdir(parents=True)
(project / "buildEnvConfig.sh").write_text(
"EP_CHIP=CV1841CP\nEP_SDK_VER=cv184x_v6.3.3\n"
"EP_ARCH=MUSL\nEP_FLASH_TYPE=nand\nNAND_FLASH_ID=0xa1d4\n",
encoding="utf-8",
)
(project / "configs" / "EPSYNCSDKConfig.sh").write_text("#!/bin/bash\n", encoding="utf-8")
(config / "cv1841cp_wevb_0015a_spinand_defconfig").write_text(
"CONFIG_DUAL_OS=y\n", encoding="utf-8"
)
partition = config / "partition" / "partition_spinand.xml"
partition.parent.mkdir()
partition.write_text(
"""<physical_partition type="spinand">
<partition label="BOOT" alias="kernel.A" size_in_kb="1024" file="kernel.bin"/>
<partition label="BOOT2" alias="kernel.B" size_in_kb="1536" file="kernel.bin"/>
<partition label="ROOTFS" alias="rootfs.A" size_in_kb="1024" file="rootfs.bin"/>
</physical_partition>
""",
encoding="utf-8",
)
(project / "imgs").symlink_to(project_guard.expected_imgs_target(name))
return project
def main() -> int:
with tempfile.TemporaryDirectory() as tmp:
base = Path(tmp)
root = base / "EmxRes"
root.mkdir()
tools = root / "tools"
tools.mkdir()
(tools / "script_completion.sh").write_text("# test\n", encoding="utf-8")
reference = make_project(root, "CV1841CP-REF_dualos")
project_report = bsp_preflight.project_preflight(
reference.name, "CV1841CP-NEW_dualos", root
)
assert project_report["passed"] is True, project_report
assert project_report["facts"]["board_build_type"] == "cv1841cp_wevb_0015a_spinand"
assert project_report["facts"]["system_form"] == "dual-os"
output_root = base / "epbuild"
build_report = bsp_preflight.build_preflight(
reference.name, "20260902_1", root, output_root
)
assert build_report["passed"] is True, build_report
assert build_report["working_directory"] == str(root.resolve())
assert build_report["command"].startswith(f"cd {root.resolve()} &&")
output = output_root / reference.name / "imgs" / "20260902_1"
output.mkdir(parents=True)
boot = output / "boot.bin"
boot.write_bytes(b"verified BSP artifact")
kernel = output / "kernel.bin"
kernel.write_bytes(b"k" * (512 * 1024))
rootfs = output / "rootfs.bin"
rootfs.write_bytes(b"r" * (256 * 1024))
conflict = bsp_preflight.build_preflight(
reference.name, "20260902_1", root, output_root
)
assert conflict["passed"] is False
clone = reference / "doc" / "bsp_adaptation" / "validation" / "clone_manifest.json"
clone.parent.mkdir(parents=True)
clone.write_text(
json.dumps({"reference_project": "CV1841CP-BASE_dualos"}), encoding="utf-8"
)
recorded = record_build_handoff.record_handoff(
reference.name,
"20260902_1",
0,
True,
root,
output_root,
None,
["NAND part number pending"],
)
assert recorded["passed"] is True, recorded
assert recorded["imgs_version_path"] == str(output.resolve())
assert recorded["image_partition_margin"]["passed"] is True
handoff_path = Path(recorded["psp_handoff_path"])
handoff = json.loads(handoff_path.read_text(encoding="utf-8"))
assert handoff["handoff_type"] == "bsp-to-psp"
assert handoff["producer"]["git"]["tracked"] is False
assert handoff["build"]["imgs_version_path"] == str(output.resolve())
assert handoff["build"]["status"] == "build_passed"
margin = handoff["validation"]["image_partition_margin"]
assert margin["passed"] is True
assert margin["required_margin_bytes"] == 512 * 1024
assert margin["partition_table_sha256"]
kernel_check = next(item for item in margin["checks"] if item["image"] == "kernel.bin")
assert kernel_check["actual_margin_bytes"] == 512 * 1024
assert kernel_check["effective_partition_size_bytes"] == 1024 * 1024
assert Path(recorded["image_partition_margin_path"]).is_file()
assert handoff["consumer_contract"]["select_latest_directory"] is False
assert handoff["artifacts"][0]["sha256"] == hashlib.sha256(boot.read_bytes()).hexdigest()
assert handoff["validation"]["hardware"] == "pending"
overflow_output = output_root / reference.name / "imgs" / "20260902_2"
overflow_output.mkdir(parents=True)
(overflow_output / "boot.bin").write_bytes(b"boot")
(overflow_output / "kernel.bin").write_bytes(b"k" * (512 * 1024))
(overflow_output / "rootfs.bin").write_bytes(b"r" * (512 * 1024 + 1))
try:
record_build_handoff.record_handoff(
reference.name,
"20260902_2",
0,
True,
root,
output_root,
None,
[],
)
except project_guard.GuardError as exc:
assert "rootfs.bin 分区余量不足" in str(exc)
else:
raise AssertionError("image with less than 512 KiB margin must fail")
assert not (
reference
/ "doc"
/ "bsp_adaptation"
/ "validation"
/ "image_partition_margin_20260902_2.json"
).exists()
original_partition = (
reference
/ "configs"
/ "cv1841cp_wevb_0015a_spinand"
/ "partition"
/ "partition_spinand.xml"
)
duplicate_partition = original_partition.with_name("partition_backup.xml")
duplicate_partition.write_text(original_partition.read_text(encoding="utf-8"), encoding="utf-8")
try:
record_build_handoff.resolve_partition_table(reference, None)
except project_guard.GuardError as exc:
assert "无法唯一定位" in str(exc)
else:
raise AssertionError("ambiguous partition tables must fail")
assert (
record_build_handoff.resolve_partition_table(
reference,
original_partition.relative_to(reference),
)
== original_partition.resolve()
)
try:
record_build_handoff.record_handoff(
reference.name,
"20260902_1",
1,
True,
root,
output_root,
None,
[],
)
except project_guard.GuardError as exc:
assert "退出码" in str(exc)
else:
raise AssertionError("failed build must not produce a PSP handoff")
print("BSP preflight and handoff behavioral tests passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())