A reader asked where FrameIO gets its list of known frame names from —
because looking at `fio.send("ALM_Req_A", ...)` it seems like the class
must hold a registry somewhere. It doesn't: FrameIO is a broker that
forwards an incoming string to the LDF object it was constructed with,
and the string lives either in the test source (Path A) or in the
generated wrapper class (Path B).
Adds section 2 "How frame names reach FrameIO" to
docs/19_frame_io_and_alm_helpers.md, between the "Three layers of
access" overview (section 1) and the API reference (formerly section 2,
now section 3). The new section contains:
- A table of where the names actually live: LDF file on disk,
LdfDatabase after parsing, caller source code. FrameIO is explicitly
NOT in that table.
- The FrameIO class skeleton showing the empty _frames cache.
- A concrete ASCII call trace of `fio.send("ALM_Req_A", ...)` from
test source -> FrameIO -> LdfDatabase -> ldfparser -> byte layout.
- Path A (stringly-typed) vs Path B (typed wrapper from gen_lin_api),
with the trade-off (typo caught at runtime vs at import time).
- The cache lifecycle (starts empty, fills lazily, one entry per
unique frame name passed in).
- A "mental model" summary calling FrameIO a generic glue layer.
Sections 3-9 renumbered to make room (3->4, 4->5, ..., 8->9). The 7.x
sub-sections under "Writing a new test" become 8.x. Updates the
stale anchor link in 14_power_supply.md
(#72-the-four-phase-test-pattern -> #82-the-four-phase-test-pattern).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ECU Tests Framework
Python-based ECU testing framework built on pytest, with a pluggable LIN communication layer (Mock and MUM, with the deprecated BabyLIN adapter retained for backward compatibility), configuration via YAML, and enhanced HTML/XML reporting with rich test metadata.
Heads-up: the BabyLIN adapter is deprecated. New tests and deployments should target MUM. BabyLIN is documented below only so existing setups can keep running while they migrate.
Highlights
- MUM (Melexis Universal Master) adapter — current default for hardware tests; networked LIN master with built-in power control
- Mock LIN adapter for fast, hardware-free development
- BabyLIN adapter (DEPRECATED) using the vendor SDK's Python wrapper
- Hex flashing scaffold you can wire to UDS
- Rich pytest fixtures and example tests
- Self-contained HTML report with Title, Requirements, Steps, and Expected Results extracted from test docstrings
- JUnit XML report for CI/CD
Quick links
- Using the framework (common runs, markers, CI, Pi):
docs/12_using_the_framework.md - Plugin overview (reporting, hooks, artifacts):
docs/11_conftest_plugin_overview.md - Power supply (Owon) usage and troubleshooting:
docs/14_power_supply.md - Report properties cheatsheet (standard keys):
docs/15_report_properties_cheatsheet.md - MUM source scripts (vendor reference): vendor/automated_lin_test/README.md
TL;DR quick start (copy/paste)
Mock (no hardware):
python -m venv .venv; .\.venv\Scripts\Activate.ps1; pip install -r requirements.txt; pytest -m "not hardware" -v
Hardware via MUM (current default):
# 1. Install Melexis 'pylin' and 'pymumclient' (see vendor/automated_lin_test/install_packages.sh)
# 2. Make sure the MUM is reachable (default IP 192.168.7.2)
$env:ECU_TESTS_CONFIG = ".\config\mum.example.yaml"; pytest -m "hardware and mum" -v
Hardware via BabyLIN (DEPRECATED — kept for existing rigs only):
# Place BabyLIN_library.py and native libs under .\vendor per vendor/README.md first
$env:ECU_TESTS_CONFIG = ".\config\babylin.example.yaml"; pytest -m "hardware and babylin" -v
Quick start (Windows PowerShell)
- Create a virtual environment and install dependencies
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
- Run the mock test suite (default interface)
python.exe -m pytest -m "not hardware" -v
- View the reports
- HTML:
reports/report.html - JUnit XML:
reports/junit.xml
Tip: You can change output via --html and --junitxml CLI options.
Quick start (WSL on Windows)
Use this approach when running from Windows Subsystem for Linux instead of PowerShell.
1. Open a WSL terminal and navigate to the project
Clone or access the repo from within WSL. If the project lives on the Windows filesystem (e.g. C:\Users\you\ecu-tests), it is available at:
cd /mnt/c/Users/<your-username>/ecu-tests
2. Create a virtual environment and install dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
3. Run the mock test suite (no hardware needed)
python -m pytest -m "not hardware" -v
4. Install Melexis packages into the venv (required for hardware tests)
pylin, pymumclient, and pylinframe are not on PyPI — they ship with the Melexis IDE.
On Windows they live at:
C:\Program Files\Melexis\Melexis IDE\plugins\com.melexis.mlxide.python_1.2.0.202408130945\python\Lib\site-packages
which WSL exposes at /mnt/c/Program Files/Melexis/Melexis IDE/....
With your venv already activated, copy the packages directly into it:
source .venv/bin/activate # skip if already active
MELEXIS_SITE="/mnt/c/Program Files/Melexis/Melexis IDE/plugins/com.melexis.mlxide.python_1.2.0.202408130945/python/Lib/site-packages"
VENV_SITE=$(python -c "import site; print(site.getsitepackages()[0])")
cp -r "$MELEXIS_SITE/pylin" "$VENV_SITE/"
cp -r "$MELEXIS_SITE/pymumclient" "$VENV_SITE/"
cp -r "$MELEXIS_SITE/pylinframe" "$VENV_SITE/"
Verify the installation:
python -c "import pylin; import pymumclient; print('OK')"
Alternative: You can also run
bash vendor/automated_lin_test/install_packages.shafter updating theMELEXIS_SITE_PACKAGESpath in that script — but the commands above are simpler and target the venv directly.
5. Run hardware tests via MUM
export ECU_TESTS_CONFIG=./config/mum.example.yaml
python -m pytest -m "hardware and mum" -v
6. Run hardware tests via BabyLIN (DEPRECATED)
Deprecated. The BabyLIN adapter is kept for backward compatibility only; new work should target MUM (step 5). The BabyLIN SDK also ships Windows-only native libraries (
.dll), so these tests cannot run under WSL unless you have a Linux-compatible.sobuild of the SDK.
export ECU_TESTS_CONFIG=./config/babylin.example.yaml
python -m pytest -m "hardware and babylin" -v
7. View reports
Open the HTML report directly in Windows from the WSL terminal:
explorer.exe reports/report.html
Or from PowerShell/CMD:
start .\reports\report.html
Reporting: Metadata in HTML
We extract these fields from each test’s docstring and render them in the HTML report:
- Title
- Description
- Requirements (e.g., REQ-001)
- Test Steps
- Expected Result
Markers like smoke, hardware, and req_00x are also displayed.
Example docstring format used by the plugin:
"""
Title: Mock LIN Interface - Send/Receive Echo Test
Description: Validates basic send/receive functionality using the mock LIN interface with echo behavior.
Requirements: REQ-001, REQ-003
Test Steps:
1. Connect to mock interface
2. Send frame ID 0x01 with data [0x55]
3. Receive the echo within 100ms
4. Assert ID and data integrity
Expected Result:
- Echoed frame matches sent frame
"""
Configuration
Default config is config/test_config.yaml. Override via the ECU_TESTS_CONFIG environment variable.
$env:ECU_TESTS_CONFIG = (Resolve-Path .\config\test_config.yaml)
MUM configuration (default for hardware)
Template: config/mum.example.yaml
interface:
type: mum
host: 192.168.7.2 # MUM IP (USB-RNDIS default)
lin_device: lin0 # MUM LIN device name
power_device: power_out0 # MUM power-control device (built-in PSU)
bitrate: 19200 # LIN baudrate
boot_settle_seconds: 0.5 # Wait after power-up before sending the first frame
frame_lengths:
0x0A: 8 # ALM_Req_A
0x11: 4 # ALM_Status
The MUM has its own power output, so power_supply.enabled: false is the
typical setting when using MUM. The Owon PSU support remains for over/under-
voltage scenarios but is independent of the LIN interface.
BabyLIN configuration (DEPRECATED)
Retained for backward compatibility. Prefer the MUM configuration above.
Template: config/babylin.example.yaml
interface:
type: babylin # deprecated; prefer "mum" or "mock"
channel: 0 # Channel index used by the SDK wrapper
bitrate: 19200 # Usually determined by SDF
sdf_path: ./vendor/Example.sdf
schedule_nr: 0 # Start this schedule on connect (-1 to skip)
LIN adapter capabilities
| Adapter | Power control | Diagnostic frames (Classic checksum) | Passive listen |
|---|---|---|---|
mock |
n/a | n/a | yes (queue-based) |
mum |
yes (power_out0) |
yes (MumLinInterface.send_raw() → ld_put_raw) |
no — receive(id) triggers a slave read |
babylin (deprecated) |
external (Owon PSU) | via SDF / BLC_sendCommand |
yes (frame queue) |
Switch to hardware profile and run only hardware tests (MUM example):
$env:ECU_TESTS_CONFIG = (Resolve-Path .\config\mum.example.yaml)
python.exe -m pytest -m hardware -v
Project structure
ecu_tests/
├── ecu_framework/ # Core framework package
│ ├── config.py # YAML config loader → typed dataclasses
│ ├── lin/
│ │ ├── base.py # LinInterface + LinFrame contract
│ │ ├── mock.py # Mock LIN adapter (no hardware)
│ │ ├── mum.py # MUM adapter (current default; Melexis pylin/pymumclient)
│ │ ├── ldf.py # LdfDatabase wrapper around ldfparser
│ │ └── babylin.py # DEPRECATED BabyLIN SDK-wrapper adapter
│ ├── power/
│ │ └── owon_psu.py # Owon PSU SCPI controller + cross-platform port resolver
│ └── flashing/
│ └── hex_flasher.py # Hex flashing scaffold
│
├── tests/
│ ├── conftest.py # Project-wide fixtures: config, lin, ldf, flash_ecu, rp
│ │
│ ├── unit/ # Pure-logic tests (no hardware)
│ │ ├── test_config_loader.py
│ │ ├── test_linframe.py
│ │ ├── test_ldf_database.py
│ │ ├── test_hex_flasher.py
│ │ ├── test_mum_adapter_mocked.py
│ │ └── test_babylin_adapter_mocked.py # deprecated path
│ │
│ ├── plugin/
│ │ └── test_conftest_plugin_artifacts.py # reporting plugin self-test
│ │
│ ├── hardware/ # Real-bench tests (MUM / PSU / ECU)
│ │ ├── conftest.py # Session-scoped autouse PSU fixture (powers the ECU)
│ │ ├── frame_io.py # FrameIO — generic LDF-driven send/receive/pack/unpack
│ │ ├── alm_helpers.py # AlmTester — ALM_Node domain helpers + constants
│ │ ├── psu_helpers.py # apply_voltage_and_settle — measure-rail-then-validate
│ │ ├── _test_case_template.py # ALM-only test starting point (not collected)
│ │ ├── _test_case_template_psu_lin.py # PSU + LIN test starting point (not collected)
│ │ ├── test_mum_alm_animation.py # ALM mode/update/LID checks via MUM
│ │ ├── test_mum_auto_addressing.py # BSM auto-addressing (NAD)
│ │ ├── test_e2e_mum_led_activate.py # MUM end-to-end power+activate
│ │ ├── test_overvolt.py # Voltage-tolerance (over/under/sweep)
│ │ ├── test_psu_voltage_settling.py # PSU settling-time characterization (-m psu_settling)
│ │ ├── test_owon_psu.py # PSU IDN + measurements (read-only)
│ │ └── test_e2e_power_on_lin_smoke.py # DEPRECATED BabyLIN E2E
│ │
│ ├── test_smoke_mock.py # Mock interface smoke + boundary
│ ├── test_babylin_hardware_smoke.py # DEPRECATED BabyLIN hardware
│ ├── test_babylin_hardware_schedule_smoke.py # DEPRECATED BabyLIN schedule flow
│ ├── test_babylin_wrapper_mock.py # DEPRECATED BabyLIN adapter w/ mock wrapper
│ └── test_hardware_placeholder.py
│
├── config/
│ ├── test_config.yaml # Default config (MUM by default)
│ ├── mum.example.yaml # MUM hardware profile
│ ├── owon_psu.example.yaml # PSU profile (copy to owon_psu.yaml)
│ ├── owon_psu.yaml # Optional per-machine PSU override
│ ├── examples.yaml # Combined mock/babylin profiles
│ └── babylin.example.yaml # DEPRECATED BabyLIN profile
│
├── docs/
│ ├── README.md # Documentation index
│ ├── 01_run_sequence.md # End-to-end run sequence
│ ├── 02_configuration_resolution.md
│ ├── 03_reporting_and_metadata.md
│ ├── 04_lin_interface_call_flow.md
│ ├── 05_architecture_overview.md
│ ├── 06_requirement_traceability.md
│ ├── 07_flash_sequence.md
│ ├── 08_babylin_internals.md # DEPRECATED
│ ├── 09_raspberry_pi_deployment.md
│ ├── 10_build_custom_image.md
│ ├── 11_conftest_plugin_overview.md
│ ├── 12_using_the_framework.md
│ ├── 13_unit_testing_guide.md
│ ├── 14_power_supply.md # PSU controller, resolver, session-managed power
│ ├── 15_report_properties_cheatsheet.md
│ ├── 16_mum_internals.md
│ ├── 17_ldf_parser.md
│ ├── 18_test_catalog.md
│ ├── 19_frame_io_and_alm_helpers.md # Hardware test helpers + four-phase pattern
│ └── DEVELOPER_COMMIT_GUIDE.md
│
├── vendor/ # Third-party + project assets
│ ├── 4SEVEN_color_lib_test.ldf # LDF used by the LIN tests
│ ├── 4SEVEN_color_lib_test.sdf # SDF for the deprecated BabyLIN path
│ ├── rgb_to_pwm.py # RGB → PWM calculator (used by ALM PWM assertions)
│ ├── led_platform.py # Platform-specific LED helpers
│ ├── Owon/
│ │ └── owon_psu_quick_demo.py # Standalone PSU demo
│ ├── automated_lin_test/ # Reference scripts (test_animation.py etc.)
│ │ ├── README.md
│ │ ├── install_packages.sh # Installs Melexis pylin/pymumclient into the venv
│ │ └── (test_*.py reference scripts)
│ ├── BabyLIN_library.py # DEPRECATED official BabyLIN SDK Python wrapper
│ ├── BLCInterfaceExample.py # DEPRECATED vendor example
│ └── BabyLIN library/ # DEPRECATED platform binaries (DLL/.so)
│
├── reports/ # Generated per-run (HTML, JUnit, summary, coverage)
│ ├── report.html
│ ├── junit.xml
│ ├── summary.md
│ └── requirements_coverage.json
│
├── scripts/
│ ├── pi_install.sh # Raspberry Pi installer
│ ├── ecu-tests.service # systemd unit
│ ├── ecu-tests.timer # systemd timer
│ ├── run_tests.sh # Convenience runner
│ ├── run_two_reports.ps1 # Split unit/non-unit report runs (Windows)
│ └── 99-babylin.rules # DEPRECATED udev rule
│
├── conftest_plugin.py # HTML metadata extraction + report customization
├── pytest.ini # Markers, addopts, junit_family=legacy
├── requirements.txt
├── README.md # ← you are here
└── TESTING_FRAMEWORK_GUIDE.md # Deep dive companion to this README
For the hardware-test layer specifically, see
docs/19_frame_io_and_alm_helpers.md
(FrameIO + AlmTester + the four-phase test pattern) and
docs/14_power_supply.md §5
(session-managed PSU lifecycle).
Usage recipes
- Run everything (mock and any non-hardware tests):
python.exe -m pytest -v
- Run by marker:
python.exe -m pytest -m "smoke" -v
python.exe -m pytest -m "req_001" -v
- Run in parallel:
python.exe -m pytest -n auto -v
- Run the plugin self-test (verifies reporting artifacts under
reports/):
python -m pytest tests\plugin\test_conftest_plugin_artifacts.py -q
- Generate separate HTML/JUnit reports for unit vs non-unit tests:
./scripts/run_two_reports.ps1
BabyLIN adapter notes (DEPRECATED)
Kept for backward compatibility. New work should target the MUM adapter.
The ecu_framework/lin/babylin.py implementation uses the official BabyLIN_library.py wrapper from the SDK. Put BabyLIN_library.py under vendor/ (or on PYTHONPATH) along with the SDK's platform-specific libraries. Configure sdf_path and schedule_nr to load an SDF and start a schedule during connect. The adapter sends frames via BLC_mon_set_xmit and receives via BLC_getNextFrameTimeout. Instantiating BabyLinInterface emits a DeprecationWarning.
Docs and references
- Guide:
TESTING_FRAMEWORK_GUIDE.md(deep dive with examples and step-by-step flows) - Reports:
reports/report.htmlandreports/junit.xml(generated on each run) - CI summary:
reports/summary.md(machine-friendly run summary) - Requirements coverage:
reports/requirements_coverage.json(requirement → tests mapping)- Tip: Open the HTML report on Windows with:
start .\reports\report.html
- Tip: Open the HTML report on Windows with:
- Configs:
config/test_config.yaml,config/mum.example.yaml,config/babylin.example.yaml(deprecated) — copy and modify for your environment - BabyLIN SDK placement and notes:
vendor/README.md(deprecated; only relevant for legacy BabyLIN rigs) - Docs index:
docs/README.md(run sequence, config resolution, reporting, call flows) - Raspberry Pi deployment:
docs/09_raspberry_pi_deployment.md - Build custom Pi image:
docs/10_build_custom_image.md - Pi scripts:
scripts/pi_install.sh,scripts/ecu-tests.service,scripts/ecu-tests.timer,scripts/run_tests.sh
Troubleshooting
- HTML report missing columns: ensure
pytest.iniincludes-p conftest_plugininaddopts. - ImportError for BabyLIN_library (DEPRECATED path): verify
vendor/BabyLIN_library.pyplacement and that required native libraries (DLL/.so) from the SDK are available on PATH/LD_LIBRARY_PATH. Consider migrating to the MUM adapter, which avoids vendor DLLs entirely. - Permission errors in PowerShell: run the venv's full Python path or adjust ExecutionPolicy for scripts.
- Import errors: activate the venv and reinstall
requirements.txt.
Owon Power Supply (SCPI) — library, config, tests, and quick demo
We provide a reusable pyserial-based library, a hardware test integrated with the central config, and a minimal quick demo script.
- Library:
ecu_framework/power/owon_psu.py(classOwonPSU,SerialParams,scan_ports) - Central config:
config/test_config.yaml(power_supplysection)- Optionally merge
config/owon_psu.yamlor setOWON_PSU_CONFIGto a YAML path
- Optionally merge
- Hardware test:
tests/hardware/test_owon_psu.py(skips unlesspower_supply.enabledis true) - quick demo:
vendor/Owon/owon_psu_quick_demo.py(readsOWON_PSU_CONFIGorconfig/owon_psu.yaml)
Quick setup (Windows PowerShell):
# Ensure dependencies
pip install -r .\requirements.txt
# Option A: configure centrally in test_config.yaml
# Edit config\test_config.yaml and set:
# power_supply.enabled: true
# power_supply.port: COM4
# Option B: use a separate machine-specific YAML
copy .\config\owon_psu.example.yaml .\config\owon_psu.yaml
# edit COM port and options in .\config\owon_psu.yaml
# Run the hardware PSU test (skips if disabled or missing port)
pytest -k test_owon_psu_idn_and_optional_set -m hardware -q
# Run the quick demo script
python .\vendor\Owon\owon_psu_quick_demo.py
YAML keys supported by power_supply:
power_supply:
enabled: true
port: COM4 # or /dev/ttyUSB0
baudrate: 115200
timeout: 1.0
eol: "\n" # or "\r\n"
parity: N # N|E|O
stopbits: 1 # 1|2
xonxoff: false
rtscts: false
dsrdtr: false
idn_substr: OWON
do_set: false
set_voltage: 5.0
set_current: 0.1
Troubleshooting:
- If
*IDN?is empty, confirm port, parity/stopbits, andeol(try\r\n). - On Windows, if COM>9, use
\\.\COM10style in some tools; here plainCOM10usually works. - Ensure only one program opens the COM port at a time.
Next steps
- Replace
HexFlasherwith a production flashing routine (UDS) - Expand tests for end-to-end ECU workflows and requirement coverage