Files
code-test-drone/utils/tests/pod_bridge/config_test.py
T
keita 477ad5f5de 新增pod_bridge吊舱桥接中间件(树莓派4B部署)
- pod_bridge: 协议G版编解码/地理位置引导律/PX4网关/串口链路/闭环主循环
- 含三项安全修复: position_time_ms拆分、表2-2锁定门控、run()异常护栏
- 测试92条全绿; 附树莓派4B(Ubuntu 24.04)部署注意事项与最小依赖清单
2026-09-23 14:38:23 +08:00

116 lines
4.3 KiB
Python

"""config_test.py —— 桥接配置加载与校验单元测试。"""
import json
from pathlib import Path
import pytest
from pod_bridge.config import (BridgeConfig, GuidanceConfig, PodSerialConfig,
load_from_json)
_DEFAULT_CONFIG = Path(__file__).resolve().parents[2] / "config" / "pod_bridge.json"
def _write(tmp_path, obj) -> str:
p = tmp_path / "cfg.json"
p.write_text(json.dumps(obj), encoding="utf-8")
return str(p)
def test_shipped_default_config_loads():
cfg = load_from_json(str(_DEFAULT_CONFIG))
assert cfg.pod_serial.baudrate == 115200
assert cfg.px4.connection_string == "udp://:14540"
assert cfg.link.attitude_downlink_hz == pytest.approx(20.0)
assert cfg.guidance.max_speed_mps == pytest.approx(5.0)
assert cfg.guidance.stop_radius_m == pytest.approx(5.0)
def test_empty_json_uses_all_defaults(tmp_path):
cfg = load_from_json(_write(tmp_path, {}))
default = BridgeConfig()
assert cfg.pod_serial == default.pod_serial
assert cfg.px4 == default.px4
assert cfg.link == default.link
assert cfg.guidance == default.guidance
def test_partial_override_keeps_other_defaults(tmp_path):
cfg = load_from_json(_write(tmp_path, {
"pod_serial": {"port": "COM3"},
"guidance": {"max_speed_mps": 8.0},
}))
assert cfg.pod_serial.port == "COM3"
assert cfg.pod_serial.baudrate == 115200 # 默认回退
assert cfg.guidance.max_speed_mps == 8.0
assert cfg.guidance.stop_radius_m == GuidanceConfig().stop_radius_m # 默认回退
def test_full_override_roundtrip(tmp_path):
obj = {
"pod_serial": {"port": "/dev/ttyUSB0", "baudrate": 115200},
"px4": {"connection_string": "serial:///dev/ttyS1:57600"},
"link": {"attitude_downlink_hz": 30.0, "telemetry_timeout_ms": 800},
"guidance": {"frequency_hz": 20.0, "stop_radius_m": 3.0,
"max_speed_mps": 7.5, "speed_kp": 0.8, "yaw_kp": 1.5},
}
cfg = load_from_json(_write(tmp_path, obj))
assert cfg.px4.connection_string == "serial:///dev/ttyS1:57600"
assert cfg.link.attitude_downlink_hz == 30.0
assert cfg.link.telemetry_timeout_ms == 800
assert cfg.guidance.frequency_hz == 20.0
assert cfg.guidance.yaw_kp == 1.5
# --- 校验错误 ---------------------------------------------------------------
def test_missing_file_raises():
with pytest.raises(ValueError, match="打开失败"):
load_from_json(str(_DEFAULT_CONFIG.parent / "no_such_file.json"))
def test_non_object_root_raises(tmp_path):
p = tmp_path / "bad.json"
p.write_text("[1, 2, 3]", encoding="utf-8")
with pytest.raises(ValueError, match="根节点必须为对象"):
load_from_json(str(p))
def test_malformed_json_raises(tmp_path):
p = tmp_path / "bad.json"
p.write_text("{not json", encoding="utf-8")
with pytest.raises(ValueError, match="解析失败"):
load_from_json(str(p))
def test_wrong_type_raises_with_field_name(tmp_path):
with pytest.raises(ValueError, match="guidance.max_speed_mps"):
load_from_json(_write(tmp_path, {"guidance": {"max_speed_mps": "fast"}}))
def test_unknown_key_rejected(tmp_path):
with pytest.raises(ValueError, match="未知字段"):
load_from_json(_write(tmp_path, {"guidance": {"max_speed": 5.0}}))
def test_empty_connection_string_raises(tmp_path):
with pytest.raises(ValueError, match="px4.connection_string"):
load_from_json(_write(tmp_path, {"px4": {"connection_string": ""}}))
@pytest.mark.parametrize("obj,field", [
({"link": {"attitude_downlink_hz": 100.0}}, "link.attitude_downlink_hz"), # >60
({"link": {"attitude_downlink_hz": 0.5}}, "link.attitude_downlink_hz"), # <1
({"guidance": {"max_speed_mps": -1.0}}, "guidance.max_speed_mps"), # 非正
({"guidance": {"stop_radius_m": -2.0}}, "guidance.stop_radius_m"), # 负
({"guidance": {"speed_derivative_filter_coef": 1.0}}, # 上界开
"guidance.speed_derivative_filter_coef"),
({"pod_serial": {"baudrate": 0}}, "pod_serial.baudrate"), # 非正
])
def test_out_of_range_raises(tmp_path, obj, field):
with pytest.raises(ValueError, match=field.replace(".", r"\.")):
load_from_json(_write(tmp_path, obj))
def test_pod_serial_default_port_is_placeholder():
assert PodSerialConfig().port == ""