326 lines
12 KiB
Python
Executable File
326 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Behavioral tests for validate_psp.py."""
|
|
|
|
import hashlib
|
|
import json
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import validate_psp
|
|
|
|
|
|
def main() -> int:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
psp = root / "psp"
|
|
(psp / "configs").mkdir(parents=True)
|
|
(psp / "scripts").mkdir()
|
|
(psp / "AiPlugin").mkdir()
|
|
artifact_root = root / "imgs" / "20260902_1"
|
|
artifact_root.mkdir(parents=True)
|
|
artifact = artifact_root / "boot.bin"
|
|
artifact.write_bytes(b"boot")
|
|
digest = hashlib.sha256(artifact.read_bytes()).hexdigest()
|
|
kernel = artifact_root / "kernel.bin"
|
|
kernel.write_bytes(b"kernel")
|
|
rootfs = artifact_root / "rootfs.bin"
|
|
rootfs.write_bytes(b"rootfs")
|
|
(psp / "psp.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"EmxMediaVersion": "EmxMedia-1",
|
|
"EmxAiPluginVersion": "EmxAiPlugin-1",
|
|
"EmxResToProduct": [{"src": str(artifact), "dst": "imgs/boot.bin"}],
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
(psp / "AiPlugin" / "profile.json").write_text(
|
|
json.dumps({"EmxSdkVersion": "EmxSdk-1"}), encoding="utf-8"
|
|
)
|
|
(psp / "configs" / "same.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"source": "psp",
|
|
"tool_cache": "/var/tmp/psp-tool-only",
|
|
"endpoint": "https://git.example/psp",
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
sensor_dir = psp / "configs" / "media" / "sensor"
|
|
sensor_dir.mkdir(parents=True)
|
|
(sensor_dir / "sensor_cfg.ini").write_text(
|
|
"[sensor]\nname = 0X201C3B40\n", encoding="utf-8"
|
|
)
|
|
expected_sensors = [("sensor", "SMS_SC4336P_MIPI_4M_30FPS_10BIT")]
|
|
(psp / "scripts" / "start.sh").write_text("#!/bin/sh\ntrue\n", encoding="utf-8")
|
|
|
|
app = root / "orgConfigs"
|
|
app.mkdir()
|
|
(app / "same.json").write_text('{"source":"app"}', encoding="utf-8")
|
|
(psp / "metadata").mkdir()
|
|
(psp / "metadata" / "allowed-overrides.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"schema_version": 1,
|
|
"overrides": [
|
|
{
|
|
"psp_path": "configs/same.json",
|
|
"application_path": "orgConfigs/same.json",
|
|
"winner": "psp",
|
|
"reason": "board config",
|
|
"confirmed_by": "PSP owner",
|
|
"confirmed_at": "2026-09-02T00:00:00+00:00",
|
|
}
|
|
],
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
handoff = root / "psp_handoff.json"
|
|
bsp_project = root / "CV1841CP-NEW_dualos"
|
|
partition = bsp_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",
|
|
)
|
|
margin_checks = []
|
|
for image in (kernel, rootfs):
|
|
size = image.stat().st_size
|
|
margin_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": margin_checks,
|
|
}
|
|
margin_path = bsp_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")
|
|
handoff.write_text(
|
|
json.dumps(
|
|
{
|
|
"schema_version": 1,
|
|
"handoff_type": "bsp-to-psp",
|
|
"producer": {
|
|
"project": "CV1841CP-NEW_dualos",
|
|
"project_path": str(bsp_project),
|
|
},
|
|
"baseline": {
|
|
"chip": "CV1841CP",
|
|
"sdk": "cv184x-v1",
|
|
"board_build_type": "cv1841cp_wevb_0015a_spinand",
|
|
"system_form": "dual-os",
|
|
},
|
|
"build": {
|
|
"version": "20260902_1",
|
|
"status": "build_passed",
|
|
"exit_code": 0,
|
|
"completion_marker_seen": True,
|
|
"imgs_version_path": str(artifact_root),
|
|
"image_partition_margin_path": str(margin_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,
|
|
"hardware": "pending",
|
|
"owner_acceptance": "pending",
|
|
"release": "not-authorized",
|
|
},
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
lock = psp / "metadata" / "bsp.lock.json"
|
|
result = validate_psp.validate_psp(
|
|
psp,
|
|
"NEW",
|
|
"EmxSdk-1",
|
|
"EmxMedia-1",
|
|
"EmxAiPlugin-1",
|
|
handoff,
|
|
lock,
|
|
"CV1841CP-NEW_dualos",
|
|
"CV1841CP",
|
|
"cv184x-v1",
|
|
"cv1841cp_wevb_0015a_spinand",
|
|
"dual-os",
|
|
[],
|
|
[(app, "configs")],
|
|
formal=False,
|
|
expected_sensors=expected_sensors,
|
|
)
|
|
assert result["passed"] is True, result
|
|
assert lock.is_file()
|
|
lock_payload = json.loads(lock.read_text(encoding="utf-8"))
|
|
assert lock_payload["image_partition_margin"]["required_margin_bytes"] == 512 * 1024
|
|
assert result["checks"]["configuration_overrides"][0]["allowed"] is True
|
|
assert any("个人或临时目录" in warning for warning in result["warnings"])
|
|
assert any(
|
|
item["source"] == "configs/same.json"
|
|
for item in result["checks"]["absolute_paths"]
|
|
)
|
|
assert all(
|
|
"git.example" not in item["path"]
|
|
for item in result["checks"]["absolute_paths"]
|
|
)
|
|
assert result["checks"]["sensor_baseline"]["sensors"][0]["matched"] is True
|
|
assert result["checks"]["sensor_baseline"]["enum_header"]["source"] == "bundled"
|
|
assert result["checks"]["sensor_baseline"]["enum_header"]["path"] == str(
|
|
validate_psp.bundled_sensor_header("cv184x")
|
|
)
|
|
cv184_items = validate_psp.parse_sensor_enum(
|
|
validate_psp.bundled_sensor_header("cv184x"), "_SNS_TYPE_E"
|
|
)
|
|
assert len(cv184_items) == 244
|
|
assert cv184_items["SMS_SC4336P_MIPI_4M_30FPS_10BIT"] == 0X201C3B40
|
|
assert cv184_items["IMGDS_MIS40C1_MIPI_4M_20FPS_1L_10BIT"] == 0X00003A00
|
|
assert "BYD_BF2253L_MIPI_1200P_30FPS_10BIT" not in cv184_items
|
|
|
|
formal = validate_psp.validate_psp(
|
|
psp,
|
|
"NEW",
|
|
"EmxSdk-1",
|
|
"EmxMedia-1",
|
|
"EmxAiPlugin-1",
|
|
handoff,
|
|
None,
|
|
"CV1841CP-NEW_dualos",
|
|
"CV1841CP",
|
|
"cv184x-v1",
|
|
"cv1841cp_wevb_0015a_spinand",
|
|
"dual-os",
|
|
[artifact_root, handoff],
|
|
[(app, "configs")],
|
|
formal=True,
|
|
expected_sensors=expected_sensors,
|
|
)
|
|
assert formal["passed"] is False
|
|
assert any(
|
|
"configs/same.json" in error and "个人或临时目录" in error
|
|
for error in formal["errors"]
|
|
)
|
|
|
|
artifact.write_bytes(b"tampered")
|
|
tampered = validate_psp.validate_psp(
|
|
psp,
|
|
"NEW",
|
|
None,
|
|
None,
|
|
None,
|
|
handoff,
|
|
None,
|
|
"CV1841CP-NEW_dualos",
|
|
"CV1841CP",
|
|
"cv184x-v1",
|
|
"cv1841cp_wevb_0015a_spinand",
|
|
"dual-os",
|
|
[],
|
|
[],
|
|
formal=False,
|
|
expected_sensors=expected_sensors,
|
|
)
|
|
assert tampered["passed"] is False
|
|
assert any("BSP handoff" in error for error in tampered["errors"])
|
|
|
|
missing_identity = validate_psp.validate_psp(
|
|
psp,
|
|
"NEW",
|
|
None,
|
|
None,
|
|
None,
|
|
handoff,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
[],
|
|
[],
|
|
formal=False,
|
|
expected_sensors=expected_sensors,
|
|
)
|
|
assert missing_identity["passed"] is False
|
|
assert any("必需身份字段" in error for error in missing_identity["errors"])
|
|
|
|
(sensor_dir / "sensor_cfg.ini").write_text(
|
|
"[sensor]\nname = SMS_SC4336P_MIPI_4M_30FPS_10BIT\n", encoding="utf-8"
|
|
)
|
|
sensor_errors: list[str] = []
|
|
validate_psp.validate_sensor_baseline(
|
|
psp,
|
|
"CV1841CP",
|
|
None,
|
|
expected_sensors,
|
|
sensor_errors,
|
|
)
|
|
assert any("必须填写" in error and "枚举值" in error for error in sensor_errors)
|
|
|
|
cv181_psp = root / "cv181-psp"
|
|
cv181_sensor_dir = cv181_psp / "configs" / "media" / "isp"
|
|
cv181_sensor_dir.mkdir(parents=True)
|
|
(cv181_sensor_dir / "sensor_cfg.ini").write_text(
|
|
"[sensor]\nname = SMS_SC4336P_MIPI_4M_30FPS_10BIT\n", encoding="utf-8"
|
|
)
|
|
cv181_errors: list[str] = []
|
|
cv181_result = validate_psp.validate_sensor_baseline(
|
|
cv181_psp,
|
|
"CV1815J",
|
|
None,
|
|
expected_sensors,
|
|
cv181_errors,
|
|
)
|
|
assert cv181_errors == [], cv181_errors
|
|
assert cv181_result["sensors"][0]["matched"] is True
|
|
assert cv181_result["enum_header"]["source"] == "bundled"
|
|
cv181_items = validate_psp.parse_sensor_enum(
|
|
validate_psp.bundled_sensor_header("cv181x"), "_SAMPLE_SNS_TYPE_E"
|
|
)
|
|
assert len(cv181_items) == 213
|
|
assert "SMS_SC4336P_MIPI_4M_30FPS_10BIT" in cv181_items
|
|
|
|
print("PSP validator tests passed")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|