Files

360 lines
14 KiB
Python
Executable File

#!/usr/bin/env python3
"""Behavioral tests for validate_psp.py."""
import hashlib
import json
import tempfile
from pathlib import Path
from unittest.mock import patch
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()
output_root = root / "outputs"
artifact_root = output_root / "CV1841CP-NEW_dualos" / "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"
)
runtime_configs = root / "runtime-configs"
runtime_configs.mkdir()
(runtime_configs / "invalid.json").write_text("{", encoding="utf-8")
(runtime_configs / "invalid.ini").write_text("[", encoding="utf-8")
(runtime_configs / "invalid.sh").write_text("if\n", encoding="utf-8")
(psp / "AiPlugin" / "configs").symlink_to(runtime_configs, target_is_directory=True)
(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")
permissions = {
"schema_version": 1,
"passed": True,
"policy": "a+rX",
"output_root": str(output_root),
"imgs_version_path": str(artifact_root),
}
permissions_path = margin_path.parent / "output_permissions_20260902_1.json"
permissions_path.write_text(json.dumps(permissions), 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),
"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",
},
}
),
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
scanned_files = {
path.relative_to(psp).as_posix()
for path in validate_psp.iter_regular_files(psp)
}
assert "AiPlugin/configs/invalid.json" not in scanned_files
assert "AiPlugin/configs/invalid.ini" not in scanned_files
assert "AiPlugin/configs/invalid.sh" not in scanned_files
with patch.object(Path, "exists", side_effect=PermissionError(13, "denied")):
exists, access_error = validate_psp.probe_path_exists(Path("/target/runtime"))
assert exists is False
assert access_error is not None and "denied" in access_error
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 lock_payload["output_permissions"]["passed"] is True
assert lock_payload["output_permissions"]["evidence_path"] == str(permissions_path)
assert output_root.stat().st_mode & 0o555 == 0o555
assert artifact.stat().st_mode & 0o444 == 0o444
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())