ecu-tests/tests/conftest.py

139 lines
5.0 KiB
Python

import os
import pathlib
import sys
import typing as t
import pytest
from ecu_framework.config import load_config, EcuTestConfig
from ecu_framework.lin.base import LinInterface
from ecu_framework.lin.mock import MockBabyLinInterface
try:
from ecu_framework.lin.babylin import BabyLinInterface # type: ignore
except Exception:
BabyLinInterface = None # type: ignore
try:
from ecu_framework.lin.mum import MumLinInterface # type: ignore
except Exception:
MumLinInterface = None # type: ignore
WORKSPACE_ROOT = pathlib.Path(__file__).resolve().parents[1]
@pytest.fixture(scope="session")
def config() -> EcuTestConfig:
cfg = load_config(str(WORKSPACE_ROOT))
return cfg
@pytest.fixture(scope="session")
def lin(config: EcuTestConfig) -> t.Iterator[LinInterface]:
iface_type = config.interface.type
if iface_type == "mock":
lin = MockBabyLinInterface(bitrate=config.interface.bitrate, channel=config.interface.channel)
elif iface_type == "babylin":
if BabyLinInterface is None:
pytest.skip("BabyLin interface not available in this environment")
lin = BabyLinInterface(
dll_path=config.interface.dll_path,
bitrate=config.interface.bitrate,
channel=config.interface.channel,
node_name=config.interface.node_name,
func_names=config.interface.func_names,
sdf_path=config.interface.sdf_path,
schedule_nr=config.interface.schedule_nr,
)
elif iface_type == "mum":
if MumLinInterface is None:
pytest.skip("MUM interface not available in this environment")
if not config.interface.host:
pytest.skip("interface.host is required when interface.type == 'mum'")
# Merge frame lengths: LDF (if any) provides defaults; YAML
# `frame_lengths` overrides on a per-id basis.
merged_lengths: dict = {}
if config.interface.ldf_path:
try:
from ecu_framework.lin.ldf import LdfDatabase
merged_lengths.update(LdfDatabase(config.interface.ldf_path).frame_lengths())
except Exception as e:
# Don't fail connect just because the LDF couldn't be parsed —
# the `ldf` fixture will surface the real error if a test asks.
sys.stderr.write(f"[lin fixture] LDF load failed, ignoring: {e!r}\n")
if config.interface.frame_lengths:
merged_lengths.update(config.interface.frame_lengths)
lin = MumLinInterface(
host=config.interface.host,
lin_device=config.interface.lin_device,
power_device=config.interface.power_device,
baudrate=config.interface.bitrate,
boot_settle_seconds=config.interface.boot_settle_seconds,
frame_lengths=merged_lengths or None,
)
else:
raise RuntimeError(f"Unknown interface type: {iface_type}")
lin.connect()
yield lin
lin.disconnect()
@pytest.fixture(scope="session")
def ldf(config: EcuTestConfig):
"""Session-scoped LDF database loaded from `interface.ldf_path`.
Tests that depend on LDF-defined frames request this fixture; tests that
don't need it can ignore it. Skips with a clear message if `ldf_path`
isn't set or the file isn't parseable.
"""
if not config.interface.ldf_path:
pytest.skip("interface.ldf_path is not set in config")
# Resolve relative paths against the workspace root for convenience.
p = pathlib.Path(config.interface.ldf_path)
if not p.is_absolute():
p = (WORKSPACE_ROOT / p).resolve()
if not p.is_file():
pytest.skip(f"LDF file not found: {p}")
try:
from ecu_framework.lin.ldf import LdfDatabase
except Exception as e:
pytest.skip(f"ldfparser not available: {e!r}")
return LdfDatabase(p)
@pytest.fixture(scope="session", autouse=False)
def flash_ecu(config: EcuTestConfig, lin: LinInterface) -> None:
if not config.flash.enabled:
pytest.skip("Flashing disabled in config")
# Lazy import to avoid dependency during mock-only runs
from ecu_framework.flashing import HexFlasher
if not config.flash.hex_path:
pytest.skip("No HEX path provided in config")
flasher = HexFlasher(lin)
ok = flasher.flash_hex(config.flash.hex_path)
if not ok:
pytest.fail("ECU flashing failed")
@pytest.fixture
def rp(record_property: "pytest.RecordProperty"):
"""Convenience reporter: attaches a key/value as a test property and echoes to captured output.
Usage in tests:
def test_something(rp):
rp("key", value)
"""
def _rp(key: str, value):
# Attach property (pytest-html will show in Properties table)
record_property(str(key), value)
# Echo to captured output for quick scanning in report details
try:
print(f"[prop] {key}={value}")
except Exception:
pass
return _rp