Documents the new layers introduced over the past several commits.
- docs/19_frame_io_and_alm_helpers.md (new): full reference for the
FrameIO and AlmTester helpers — three access levels (high/mid/low),
full API tables, fixture wiring, cookbook patterns, and §7
describing the four-phase SETUP/PROCEDURE/ASSERT/TEARDOWN test
pattern with the three template flavors plus a §7.4 link to the
PSU+LIN template.
- docs/14_power_supply.md: rewritten and expanded.
§3 cross-platform port resolution (Windows / WSL1 / WSL2 +
usbipd-win / Linux native compatibility table)
§4 auto-detection via idn_substr
§5 session-managed power: contract for tests, must-not list,
what changed in the existing tests
§6 the settle-then-validate pattern: two-delays table (PSU
bench-dependent vs ECU firmware-dependent), copy-paste
example, tuning guidance for ECU_VALIDATION_TIME_S
§6 PSU settling characterization (-m psu_settling)
§7 library API reference table + safe_off_on_close
§9 troubleshooting expanded with WSL2 usbipd-win + dialout
- docs/18_test_catalog.md: voltage-tolerance section refreshed for
the settle-then-validate shape, new "Hardware – PSU settling
(opt-in)" category, new §8 "Hardware-test infrastructure"
documenting conftest.py, frame_io.py, alm_helpers.py,
psu_helpers.py, and both templates.
- docs/05_architecture_overview.md: components list split into
framework core / hardware test layer / artifacts. Mermaid diagram
gained a Hardware-test helpers subgraph showing FrameIO,
AlmTester, rgb_to_pwm, and the templates. Data/control flow
summary describes the session-managed PSU and the helper layer.
- docs/15_report_properties_cheatsheet.md: PSU section split into
per-test (function-scoped rp) and module-scoped (testsuite
property) blocks; added psu_resolved_port, psu_resolved_idn,
psu_settled_s, validation_time_s.
- docs/README.md: links to the new doc 19.
- README.md, TESTING_FRAMEWORK_GUIDE.md: project-structure trees
expanded to show the full current layout — every file and
directory under tests/hardware/ (conftest, helpers, templates,
tests), tests/unit/, config/, docs/, scripts/, and vendor/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
20 KiB
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