diff --git a/.gitignore b/.gitignore index ba953fd..f12e5ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,182 +1,182 @@ -# ---> Python -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -#uv.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# --- Project specific --- -# Test run artifacts -reports/ -!reports/.gitkeep - -# Vendor binaries (keep headers/docs and keep .dll from the SDK for now) -vendor/**/*.lib -vendor/**/*.pdb - -# Optional firmware blobs (uncomment if you don't want to track) -# firmware/ - +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# --- Project specific --- +# Test run artifacts +reports/ +!reports/.gitkeep + +# Vendor binaries (keep headers/docs and keep .dll from the SDK for now) +vendor/**/*.lib +vendor/**/*.pdb + +# Optional firmware blobs (uncomment if you don't want to track) +# firmware/ + diff --git a/README.md b/README.md index ff62967..0cd5dc8 100644 --- a/README.md +++ b/README.md @@ -1,285 +1,326 @@ -# ECU Tests Framework - -Python-based ECU testing framework built on pytest, with a pluggable LIN communication layer (Mock and BabyLin), configuration via YAML, and enhanced HTML/XML reporting with rich test metadata. - -## Highlights - -- Mock LIN adapter for fast, hardware-free development -- Real BabyLIN adapter using the SDK's official Python wrapper (BabyLIN_library.py) -- 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` - -## TL;DR quick start (copy/paste) - -Mock (no hardware): - -```powershell -python -m venv .venv; .\.venv\Scripts\Activate.ps1; pip install -r requirements.txt; pytest -m "not hardware" -v -``` - -Hardware (BabyLIN SDK): - -```powershell -# 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) - -1) Create a virtual environment and install dependencies - -```powershell -python -m venv .venv -.\.venv\Scripts\Activate.ps1 -pip install -r requirements.txt -``` - -2) Run the mock test suite (default interface) - -```powershell -python.exe -m pytest -m "not hardware" -v -``` - -3) View the reports - -- HTML: `reports/report.html` -- JUnit XML: `reports/junit.xml` - -Tip: You can change output via `--html` and `--junitxml` CLI options. - -## 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: - -```python -""" -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. - -```powershell -$env:ECU_TESTS_CONFIG = (Resolve-Path .\config\test_config.yaml) -``` - -BabyLIN configuration template: `config/babylin.example.yaml` - -```yaml -interface: - type: babylin # 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 -``` - -Switch to hardware profile and run only hardware tests: - -```powershell -$env:ECU_TESTS_CONFIG = (Resolve-Path .\config\babylin.example.yaml) -python.exe -m pytest -m hardware -v -``` - -## Project structure - -``` -ecu_tests/ -├── ecu_framework/ -│ ├── config.py # YAML config loader -│ ├── power/ -│ │ └── owon_psu.py # Owon PSU serial SCPI controller (library) -│ ├── lin/ -│ │ ├── base.py # LinInterface + LinFrame -│ │ ├── mock.py # Mock LIN adapter -│ │ └── babylin.py # BabyLIN SDK-wrapper adapter (uses BabyLIN_library.py) -│ └── flashing/ -│ └── hex_flasher.py # Hex flashing scaffold -├── tests/ -│ ├── conftest.py # Shared fixtures -│ ├── test_smoke_mock.py # Mock interface smoke and boundary tests -│ ├── test_babylin_hardware_smoke.py # Hardware smoke tests -│ ├── test_babylin_hardware_schedule_smoke.py # Hardware schedule flow -│ ├── test_babylin_wrapper_mock.py # SDK adapter with mock wrapper -│ ├── plugin/ -│ │ └── test_conftest_plugin_artifacts.py # Plugin self-test (reports artifacts) -│ ├── unit/ -│ │ ├── test_config_loader.py # Config loader unit tests -│ │ ├── test_linframe.py # LIN frame dataclass/logic -│ │ ├── test_hex_flasher.py # Hex flasher scaffolding -│ │ └── test_babylin_adapter_mocked.py # BabyLIN adapter with mocks -│ └── hardware/ -│ └── test_owon_psu.py # Owon PSU hardware test (uses central config) -├── config/ -│ ├── test_config.yaml # Default config -│ ├── babylin.example.yaml # BabyLIN hardware template -│ ├── owon_psu.example.yaml # Owon PSU example (copy to owon_psu.yaml) -│ └── owon_psu.yaml # Optional machine-specific PSU config -├── vendor/ # Place SDK wrapper and platform libs here -│ ├── Owon/ -│ │ └── owon_psu_quick_demo.py # Quick PSU demo using the library & YAML -│ ├── BabyLIN_library.py # Official SDK Python wrapper -│ └── BabyLIN library/ # Platform-specific binaries from SDK (DLL/.so) -├── reports/ # Generated reports -│ ├── report.html -│ └── junit.xml -├── conftest_plugin.py # HTML metadata extraction & rendering -├── pytest.ini # Markers and default addopts -├── requirements.txt -└── README.md -``` - -## Usage recipes - -- Run everything (mock and any non-hardware tests): - -```powershell -python.exe -m pytest -v -``` - -- Run by marker: - -```powershell -python.exe -m pytest -m "smoke" -v -python.exe -m pytest -m "req_001" -v -``` - -- Run in parallel: - -```powershell -python.exe -m pytest -n auto -v -``` - -- Run the plugin self-test (verifies reporting artifacts under `reports/`): - -```powershell -python -m pytest tests\plugin\test_conftest_plugin_artifacts.py -q -``` - -- Generate separate HTML/JUnit reports for unit vs non-unit tests: - -```powershell -./scripts/run_two_reports.ps1 -``` - -## BabyLIN adapter notes - -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`. - -## Docs and references - -- Guide: `TESTING_FRAMEWORK_GUIDE.md` (deep dive with examples and step-by-step flows) -- Reports: `reports/report.html` and `reports/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` -- Configs: `config/test_config.yaml`, `config/babylin.example.yaml` (copy and modify for your environment) -- BabyLIN SDK placement and notes: `vendor/README.md` -- 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.ini` includes `-p conftest_plugin` in `addopts`. -- ImportError for BabyLIN_library: verify `vendor/BabyLIN_library.py` placement and that required native libraries (DLL/.so) from the SDK are available on PATH/LD_LIBRARY_PATH. -- 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` (class `OwonPSU`, `SerialParams`, `scan_ports`) -- Central config: `config/test_config.yaml` (`power_supply` section) - - Optionally merge `config/owon_psu.yaml` or set `OWON_PSU_CONFIG` to a YAML path -- Hardware test: `tests/hardware/test_owon_psu.py` (skips unless `power_supply.enabled` is true) -- quick demo: `vendor/Owon/owon_psu_quick_demo.py` (reads `OWON_PSU_CONFIG` or `config/owon_psu.yaml`) - -Quick setup (Windows PowerShell): - -```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`: - -```yaml -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, and `eol` (try `\r\n`). -- On Windows, if COM>9, use `\\.\COM10` style in some tools; here plain `COM10` usually works. -- Ensure only one program opens the COM port at a time. - -## Next steps - -- Replace `HexFlasher` with a production flashing routine (UDS) -- Expand tests for end-to-end ECU workflows and requirement coverage +# ECU Tests Framework + +Python-based ECU testing framework built on pytest, with a pluggable LIN communication layer (Mock, MUM, and legacy BabyLIN), configuration via YAML, and enhanced HTML/XML reporting with rich test metadata. + +## 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 (legacy) 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](vendor/automated_lin_test/README.md) + +## TL;DR quick start (copy/paste) + +Mock (no hardware): + +```powershell +python -m venv .venv; .\.venv\Scripts\Activate.ps1; pip install -r requirements.txt; pytest -m "not hardware" -v +``` + +Hardware via MUM (current default): + +```powershell +# 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 (legacy): + +```powershell +# 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) + +1) Create a virtual environment and install dependencies + +```powershell +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +pip install -r requirements.txt +``` + +2) Run the mock test suite (default interface) + +```powershell +python.exe -m pytest -m "not hardware" -v +``` + +3) View the reports + +- HTML: `reports/report.html` +- JUnit XML: `reports/junit.xml` + +Tip: You can change output via `--html` and `--junitxml` CLI options. + +## 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: + +```python +""" +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. + +```powershell +$env:ECU_TESTS_CONFIG = (Resolve-Path .\config\test_config.yaml) +``` + +### MUM configuration (default for hardware) + +Template: `config/mum.example.yaml` + +```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 (legacy) + +Template: `config/babylin.example.yaml` + +```yaml +interface: + type: babylin # or "mock", or "mum" + 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` | external (Owon PSU) | via SDF / `BLC_sendCommand` | yes (frame queue) | + +Switch to hardware profile and run only hardware tests: + +```powershell +$env:ECU_TESTS_CONFIG = (Resolve-Path .\config\babylin.example.yaml) +python.exe -m pytest -m hardware -v +``` + +## Project structure + +``` +ecu_tests/ +├── ecu_framework/ +│ ├── config.py # YAML config loader +│ ├── power/ +│ │ └── owon_psu.py # Owon PSU serial SCPI controller (library) +│ ├── lin/ +│ │ ├── base.py # LinInterface + LinFrame +│ │ ├── mock.py # Mock LIN adapter +│ │ └── babylin.py # BabyLIN SDK-wrapper adapter (uses BabyLIN_library.py) +│ └── flashing/ +│ └── hex_flasher.py # Hex flashing scaffold +├── tests/ +│ ├── conftest.py # Shared fixtures +│ ├── test_smoke_mock.py # Mock interface smoke and boundary tests +│ ├── test_babylin_hardware_smoke.py # Hardware smoke tests +│ ├── test_babylin_hardware_schedule_smoke.py # Hardware schedule flow +│ ├── test_babylin_wrapper_mock.py # SDK adapter with mock wrapper +│ ├── plugin/ +│ │ └── test_conftest_plugin_artifacts.py # Plugin self-test (reports artifacts) +│ ├── unit/ +│ │ ├── test_config_loader.py # Config loader unit tests +│ │ ├── test_linframe.py # LIN frame dataclass/logic +│ │ ├── test_hex_flasher.py # Hex flasher scaffolding +│ │ └── test_babylin_adapter_mocked.py # BabyLIN adapter with mocks +│ └── hardware/ +│ └── test_owon_psu.py # Owon PSU hardware test (uses central config) +├── config/ +│ ├── test_config.yaml # Default config +│ ├── babylin.example.yaml # BabyLIN hardware template +│ ├── owon_psu.example.yaml # Owon PSU example (copy to owon_psu.yaml) +│ └── owon_psu.yaml # Optional machine-specific PSU config +├── vendor/ # Place SDK wrapper and platform libs here +│ ├── Owon/ +│ │ └── owon_psu_quick_demo.py # Quick PSU demo using the library & YAML +│ ├── BabyLIN_library.py # Official SDK Python wrapper +│ └── BabyLIN library/ # Platform-specific binaries from SDK (DLL/.so) +├── reports/ # Generated reports +│ ├── report.html +│ └── junit.xml +├── conftest_plugin.py # HTML metadata extraction & rendering +├── pytest.ini # Markers and default addopts +├── requirements.txt +└── README.md +``` + +## Usage recipes + +- Run everything (mock and any non-hardware tests): + +```powershell +python.exe -m pytest -v +``` + +- Run by marker: + +```powershell +python.exe -m pytest -m "smoke" -v +python.exe -m pytest -m "req_001" -v +``` + +- Run in parallel: + +```powershell +python.exe -m pytest -n auto -v +``` + +- Run the plugin self-test (verifies reporting artifacts under `reports/`): + +```powershell +python -m pytest tests\plugin\test_conftest_plugin_artifacts.py -q +``` + +- Generate separate HTML/JUnit reports for unit vs non-unit tests: + +```powershell +./scripts/run_two_reports.ps1 +``` + +## BabyLIN adapter notes + +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`. + +## Docs and references + +- Guide: `TESTING_FRAMEWORK_GUIDE.md` (deep dive with examples and step-by-step flows) +- Reports: `reports/report.html` and `reports/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` +- Configs: `config/test_config.yaml`, `config/babylin.example.yaml` (copy and modify for your environment) +- BabyLIN SDK placement and notes: `vendor/README.md` +- 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.ini` includes `-p conftest_plugin` in `addopts`. +- ImportError for BabyLIN_library: verify `vendor/BabyLIN_library.py` placement and that required native libraries (DLL/.so) from the SDK are available on PATH/LD_LIBRARY_PATH. +- 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` (class `OwonPSU`, `SerialParams`, `scan_ports`) +- Central config: `config/test_config.yaml` (`power_supply` section) + - Optionally merge `config/owon_psu.yaml` or set `OWON_PSU_CONFIG` to a YAML path +- Hardware test: `tests/hardware/test_owon_psu.py` (skips unless `power_supply.enabled` is true) +- quick demo: `vendor/Owon/owon_psu_quick_demo.py` (reads `OWON_PSU_CONFIG` or `config/owon_psu.yaml`) + +Quick setup (Windows PowerShell): + +```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`: + +```yaml +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, and `eol` (try `\r\n`). +- On Windows, if COM>9, use `\\.\COM10` style in some tools; here plain `COM10` usually works. +- Ensure only one program opens the COM port at a time. + +## Next steps + +- Replace `HexFlasher` with a production flashing routine (UDS) +- Expand tests for end-to-end ECU workflows and requirement coverage diff --git a/TESTING_FRAMEWORK_GUIDE.md b/TESTING_FRAMEWORK_GUIDE.md index 85605d0..1474937 100644 --- a/TESTING_FRAMEWORK_GUIDE.md +++ b/TESTING_FRAMEWORK_GUIDE.md @@ -1,359 +1,359 @@ -# ECU Testing Framework - Complete Guide - -## Overview - -This comprehensive ECU Testing Framework provides a robust solution for testing Electronic Control Units (ECUs) using pytest with BabyLIN LIN bus communication. The framework includes detailed test documentation, enhanced reporting, mock interfaces for development, and real hardware integration capabilities. - -## Framework Features - -### ✅ **Complete Implementation Status** -- **✅ pytest-based testing framework** with custom plugins -- **✅ BabyLIN LIN communication integration** via the official SDK Python wrapper (`BabyLIN_library.py`) -- **✅ Mock interface for hardware-independent development** -- **✅ Enhanced HTML/XML reporting with test metadata** -- **✅ Detailed test documentation extraction** -- **✅ Configuration management with YAML** -- **✅ Hex file flashing capabilities (scaffold)** -- **✅ Custom pytest markers for requirement traceability** - -## Enhanced Reporting System - -### Test Metadata Integration - -The framework automatically extracts detailed test information from docstrings and integrates it into reports: - -**HTML Report Features:** -- **Title Column**: Clear test descriptions extracted from docstrings -- **Requirements Column**: Requirement traceability (REQ-001, REQ-002, etc.) -- **Enhanced Test Details**: Description, test steps, and expected results -- **Marker Integration**: Custom pytest markers for categorization - -**Example Test Documentation Format:** -```python -@pytest.mark.smoke -@pytest.mark.req_001 -def test_mock_send_receive_echo(self, mock_interface): - """ - Title: Mock LIN Interface - Send/Receive Echo Test - - Description: Validates basic send/receive functionality using the mock - LIN interface with echo behavior for development testing. - - Requirements: REQ-001, REQ-003 - - Test Steps: - 1. Connect to mock LIN interface - 2. Send a test frame with ID 0x01 and data [0x55] - 3. Receive the echoed frame within 100ms timeout - 4. Verify frame ID and data integrity - - Expected Result: - - Frame should be echoed back successfully - - Received data should match sent data exactly - - Operation should complete within timeout period - """ -``` - -### Report Generation - -**HTML Report (`reports/report.html`):** -- Interactive table with sortable columns -- Test titles and requirements clearly visible -- Execution duration and status tracking -- Enhanced metadata from docstrings - -**XML Report (`reports/junit.xml`):** -- Standard JUnit XML format for CI/CD integration -- Test execution data and timing information -- Compatible with most CI systems (Jenkins, GitLab CI, etc.) - -## Project Structure - -``` -ecu_tests/ -├── ecu_framework/ # Core framework package -│ ├── config.py # YAML configuration management -│ ├── lin/ # LIN communication interfaces -│ │ ├── base.py # Abstract LinInterface definition -│ │ ├── mock.py # Mock interface for development -│ │ └── babylin.py # Real BabyLin hardware interface -│ └── flashing/ # Hex file flashing capabilities -│ └── hex_flasher.py # ECU flash programming -├── tests/ # Test suite -│ ├── conftest.py # pytest fixtures and configuration -│ ├── test_smoke_mock.py # Mock interface validation tests -│ ├── test_babylin_hardware_smoke.py # Hardware smoke tests -│ └── test_hardware_placeholder.py # Future hardware tests -├── config/ # Configuration files -│ ├── test_config.yaml # Main test configuration -│ └── babylin.example.yaml # BabyLin configuration template -├── vendor/ # BabyLIN SDK placement -| ├── BabyLIN_library.py # Official SDK Python wrapper -| └── platform libs # OS-specific native libs (DLL/.so/.dylib) -├── reports/ # Generated test reports -│ ├── report.html # Enhanced HTML report -│ └── junit.xml # JUnit XML report -├── conftest_plugin.py # Custom pytest plugin for enhanced reporting -├── pytest.ini # pytest configuration with custom markers -├── requirements.txt # Python dependencies -└── README.md # Project documentation -``` - -## Running Tests - -### Basic Test Execution - -```powershell -# Run all tests with verbose output -python -m pytest -v - -# Run specific test suite -python -m pytest tests\test_smoke_mock.py -v - -# Run tests with specific markers -python -m pytest -m "smoke" -v -python -m pytest -m "req_001" -v - -# Run hardware tests (requires BabyLIN hardware); join with adapter marker -python -m pytest -m "hardware and babylin" -v -``` - -### Unit Tests (fast, no hardware) - -Run only unit tests using the dedicated marker or by path: - -```powershell -# By marker -python -m pytest -m unit -q - -# By path -python -m pytest tests\unit -q - -# Plugin self-tests (verifies reporting artifacts) -python -m pytest tests\plugin -q -``` - -Reports still go to `reports/` (HTML and JUnit per defaults). Open the HTML on Windows with: - -```powershell -start .\reports\report.html -``` - -Coverage: enabled by default via pytest.ini. To disable locally: - -```powershell -python -m pytest -q -o addopts="" -``` - -Optional HTML coverage: - -```powershell -python -m pytest --cov=ecu_framework --cov-report=html -q -start .\htmlcov\index.html -``` - -See also: `docs/13_unit_testing_guide.md` for more details and examples. - -### Report Generation - -Tests automatically generate enhanced reports: -- **HTML Report**: `reports/report.html` - Interactive report with metadata -- **XML Report**: `reports/junit.xml` - CI/CD compatible format - -## Configuration - -### Test Configuration (`config/test_config.yaml`) - -```yaml -interface: - type: mock # or babylin for hardware - timeout: 1.0 - -flash: - hex_file_path: firmware/ecu_firmware.hex - flash_timeout: 30.0 - -ecu: - name: Test ECU - lin_id_range: [0x01, 0x3F] -``` - -### BabyLIN Configuration (`config/babylin.example.yaml`) - -```yaml -interface: - type: babylin - channel: 0 # channel index used by the SDK wrapper - bitrate: 19200 # typically set by SDF - sdf_path: ./vendor/Example.sdf - schedule_nr: 0 # schedule to start on connect -``` - -## Test Categories - -### 1. Mock Interface Tests (`test_smoke_mock.py`) - -**Purpose**: Hardware-independent development and validation -- ✅ Send/receive echo functionality -- ✅ Master request/response testing -- ✅ Timeout behavior validation -- ✅ Frame validation boundary testing -- ✅ Parameterized boundary tests for comprehensive coverage - -**Status**: **7 tests passing** - Complete implementation - -### 2. Hardware Smoke Tests (`test_babylin_hardware_smoke.py`) - -**Purpose**: Basic BabyLIN hardware connectivity validation -- ✅ SDK wrapper import and device open -- ✅ Interface connection establishment -- ✅ Basic send/receive operations -- ✅ Error handling and cleanup - -**Status**: Ready for hardware testing - -### 3. Hardware Integration Tests (`test_hardware_placeholder.py`) - -**Purpose**: Full ECU testing workflow with real hardware -- ECU flashing with hex files -- Communication protocol validation -- Diagnostic command testing -- Performance and stress testing - -**Status**: Framework ready, awaiting ECU specifications - -## Custom Pytest Markers - -The framework includes custom markers for test categorization and requirement traceability: - -```python -# In pytest.ini -markers = - smoke: Basic functionality tests - integration: Integration tests requiring hardware - hardware: Tests requiring physical BabyLin hardware - babylin: Tests targeting the BabyLIN SDK adapter - unit: Fast unit tests (no hardware) - boundary: Boundary condition and edge case tests - req_001: Tests validating requirement REQ-001 (LIN Interface Basic Operations) - req_002: Tests validating requirement REQ-002 (Master Request/Response) - req_003: Tests validating requirement REQ-003 (Frame Validation) - req_004: Tests validating requirement REQ-004 (Timeout Handling) -``` - -## BabyLIN Integration Details - -### SDK Python wrapper - -The framework uses the official SDK Python wrapper `BabyLIN_library.py` (placed under `vendor/`) and calls its BLC_* APIs. - -Key calls in the adapter (`ecu_framework/lin/babylin.py`): -- `BLC_getBabyLinPorts`, `BLC_openPort` — discovery and open -- `BLC_loadSDF`, `BLC_getChannelHandle`, `BLC_sendCommand('start schedule N;')` — SDF + scheduling -- `BLC_mon_set_xmit` — transmit -- `BLC_getNextFrameTimeout` — receive -- `BLC_sendRawMasterRequest` — master request (length then bytes) - -## Development Workflow - -### 1. Development Phase -```powershell -# Use mock interface for development -python -m pytest tests\test_smoke_mock.py -v -``` - -### 2. Hardware Integration Phase -```powershell -# Test with real BabyLIN hardware -python -m pytest -m "hardware and babylin" -v -``` - -### 3. Full System Testing -```powershell -# Complete test suite including ECU flashing -python -m pytest -v -``` - -## Enhanced Reporting Output Example - -The enhanced HTML report includes: - -| Result | Test | Title | Requirements | Duration | Links | -|--------|------|-------|--------------|----------|--------| -| ✅ Passed | test_mock_send_receive_echo | Mock LIN Interface - Send/Receive Echo Test | REQ-001, REQ-003 | 1 ms | | -| ✅ Passed | test_mock_request_synthesized_response | Mock LIN Interface - Master Request Response Test | REQ-002 | 0 ms | | -| ✅ Passed | test_mock_receive_timeout_behavior | Mock LIN Interface - Receive Timeout Test | REQ-004 | 106 ms | | - -## Framework Validation Results - -**Current Status**: ✅ **All core features implemented and tested** - -**Mock Interface Tests**: 7/7 passing (0.14s execution time) -- Send/receive operations: ✅ Working -- Timeout handling: ✅ Working -- Frame validation: ✅ Working -- Boundary testing: ✅ Working - -**Enhanced Reporting**: ✅ **Fully functional** -- HTML report with metadata: ✅ Working -- XML report generation: ✅ Working -- Custom pytest plugin: ✅ Working -- Docstring metadata extraction: ✅ Working - -**Configuration System**: ✅ **Complete** -- YAML configuration loading: ✅ Working -- Environment variable override: ✅ Working -- BabyLIN SDF/schedule configuration: ✅ Working - - Power supply (PSU) configuration: ✅ Working (see `config/test_config.yaml` → `power_supply`) - -## Owon Power Supply (PSU) Integration - -The framework includes a serial SCPI controller for Owon PSUs and a hardware test wired to the central config. - -- Library: `ecu_framework/power/owon_psu.py` (pyserial) -- Config: `config/test_config.yaml` (`power_supply` section) - - Optionally merge machine-specific settings from `config/owon_psu.yaml` or env `OWON_PSU_CONFIG` -- Hardware test: `tests/hardware/test_owon_psu.py` (skips unless `power_supply.enabled` and `port` present) -- quick demo: `vendor/Owon/owon_psu_quickdemo.py` - -Quick run: - -```powershell -pip install -r .\requirements.txt -copy .\config\owon_psu.example.yaml .\config\owon_psu.yaml -# edit COM port in .\config\owon_psu.yaml -pytest -k test_owon_psu_idn_and_optional_set -m hardware -q -python .\vendor\Owon\owon_psu_quick_demo.py -``` - -Common config keys: - -```yaml -power_supply: - enabled: true - port: COM4 - baudrate: 115200 - timeout: 1.0 - eol: "\n" - parity: N - stopbits: 1 - idn_substr: OWON -``` - -## Next Steps - -1. **Hardware Testing**: Connect BabyLin hardware and validate hardware smoke tests -2. **ECU Integration**: Define ECU-specific communication protocols and diagnostic commands -3. **Hex Flashing**: Implement complete hex file flashing workflow -4. **CI/CD Integration**: Set up automated testing pipeline with generated reports - -## Dependencies - -``` -pytest>=8.4.2 -pytest-html>=4.1.1 -pytest-xdist>=3.8.0 -pyyaml>=6.0.2 -``` - +# ECU Testing Framework - Complete Guide + +## Overview + +This comprehensive ECU Testing Framework provides a robust solution for testing Electronic Control Units (ECUs) using pytest with BabyLIN LIN bus communication. The framework includes detailed test documentation, enhanced reporting, mock interfaces for development, and real hardware integration capabilities. + +## Framework Features + +### ✅ **Complete Implementation Status** +- **✅ pytest-based testing framework** with custom plugins +- **✅ BabyLIN LIN communication integration** via the official SDK Python wrapper (`BabyLIN_library.py`) +- **✅ Mock interface for hardware-independent development** +- **✅ Enhanced HTML/XML reporting with test metadata** +- **✅ Detailed test documentation extraction** +- **✅ Configuration management with YAML** +- **✅ Hex file flashing capabilities (scaffold)** +- **✅ Custom pytest markers for requirement traceability** + +## Enhanced Reporting System + +### Test Metadata Integration + +The framework automatically extracts detailed test information from docstrings and integrates it into reports: + +**HTML Report Features:** +- **Title Column**: Clear test descriptions extracted from docstrings +- **Requirements Column**: Requirement traceability (REQ-001, REQ-002, etc.) +- **Enhanced Test Details**: Description, test steps, and expected results +- **Marker Integration**: Custom pytest markers for categorization + +**Example Test Documentation Format:** +```python +@pytest.mark.smoke +@pytest.mark.req_001 +def test_mock_send_receive_echo(self, mock_interface): + """ + Title: Mock LIN Interface - Send/Receive Echo Test + + Description: Validates basic send/receive functionality using the mock + LIN interface with echo behavior for development testing. + + Requirements: REQ-001, REQ-003 + + Test Steps: + 1. Connect to mock LIN interface + 2. Send a test frame with ID 0x01 and data [0x55] + 3. Receive the echoed frame within 100ms timeout + 4. Verify frame ID and data integrity + + Expected Result: + - Frame should be echoed back successfully + - Received data should match sent data exactly + - Operation should complete within timeout period + """ +``` + +### Report Generation + +**HTML Report (`reports/report.html`):** +- Interactive table with sortable columns +- Test titles and requirements clearly visible +- Execution duration and status tracking +- Enhanced metadata from docstrings + +**XML Report (`reports/junit.xml`):** +- Standard JUnit XML format for CI/CD integration +- Test execution data and timing information +- Compatible with most CI systems (Jenkins, GitLab CI, etc.) + +## Project Structure + +``` +ecu_tests/ +├── ecu_framework/ # Core framework package +│ ├── config.py # YAML configuration management +│ ├── lin/ # LIN communication interfaces +│ │ ├── base.py # Abstract LinInterface definition +│ │ ├── mock.py # Mock interface for development +│ │ └── babylin.py # Real BabyLin hardware interface +│ └── flashing/ # Hex file flashing capabilities +│ └── hex_flasher.py # ECU flash programming +├── tests/ # Test suite +│ ├── conftest.py # pytest fixtures and configuration +│ ├── test_smoke_mock.py # Mock interface validation tests +│ ├── test_babylin_hardware_smoke.py # Hardware smoke tests +│ └── test_hardware_placeholder.py # Future hardware tests +├── config/ # Configuration files +│ ├── test_config.yaml # Main test configuration +│ └── babylin.example.yaml # BabyLin configuration template +├── vendor/ # BabyLIN SDK placement +| ├── BabyLIN_library.py # Official SDK Python wrapper +| └── platform libs # OS-specific native libs (DLL/.so/.dylib) +├── reports/ # Generated test reports +│ ├── report.html # Enhanced HTML report +│ └── junit.xml # JUnit XML report +├── conftest_plugin.py # Custom pytest plugin for enhanced reporting +├── pytest.ini # pytest configuration with custom markers +├── requirements.txt # Python dependencies +└── README.md # Project documentation +``` + +## Running Tests + +### Basic Test Execution + +```powershell +# Run all tests with verbose output +python -m pytest -v + +# Run specific test suite +python -m pytest tests\test_smoke_mock.py -v + +# Run tests with specific markers +python -m pytest -m "smoke" -v +python -m pytest -m "req_001" -v + +# Run hardware tests (requires BabyLIN hardware); join with adapter marker +python -m pytest -m "hardware and babylin" -v +``` + +### Unit Tests (fast, no hardware) + +Run only unit tests using the dedicated marker or by path: + +```powershell +# By marker +python -m pytest -m unit -q + +# By path +python -m pytest tests\unit -q + +# Plugin self-tests (verifies reporting artifacts) +python -m pytest tests\plugin -q +``` + +Reports still go to `reports/` (HTML and JUnit per defaults). Open the HTML on Windows with: + +```powershell +start .\reports\report.html +``` + +Coverage: enabled by default via pytest.ini. To disable locally: + +```powershell +python -m pytest -q -o addopts="" +``` + +Optional HTML coverage: + +```powershell +python -m pytest --cov=ecu_framework --cov-report=html -q +start .\htmlcov\index.html +``` + +See also: `docs/13_unit_testing_guide.md` for more details and examples. + +### Report Generation + +Tests automatically generate enhanced reports: +- **HTML Report**: `reports/report.html` - Interactive report with metadata +- **XML Report**: `reports/junit.xml` - CI/CD compatible format + +## Configuration + +### Test Configuration (`config/test_config.yaml`) + +```yaml +interface: + type: mock # or babylin for hardware + timeout: 1.0 + +flash: + hex_file_path: firmware/ecu_firmware.hex + flash_timeout: 30.0 + +ecu: + name: Test ECU + lin_id_range: [0x01, 0x3F] +``` + +### BabyLIN Configuration (`config/babylin.example.yaml`) + +```yaml +interface: + type: babylin + channel: 0 # channel index used by the SDK wrapper + bitrate: 19200 # typically set by SDF + sdf_path: ./vendor/Example.sdf + schedule_nr: 0 # schedule to start on connect +``` + +## Test Categories + +### 1. Mock Interface Tests (`test_smoke_mock.py`) + +**Purpose**: Hardware-independent development and validation +- ✅ Send/receive echo functionality +- ✅ Master request/response testing +- ✅ Timeout behavior validation +- ✅ Frame validation boundary testing +- ✅ Parameterized boundary tests for comprehensive coverage + +**Status**: **7 tests passing** - Complete implementation + +### 2. Hardware Smoke Tests (`test_babylin_hardware_smoke.py`) + +**Purpose**: Basic BabyLIN hardware connectivity validation +- ✅ SDK wrapper import and device open +- ✅ Interface connection establishment +- ✅ Basic send/receive operations +- ✅ Error handling and cleanup + +**Status**: Ready for hardware testing + +### 3. Hardware Integration Tests (`test_hardware_placeholder.py`) + +**Purpose**: Full ECU testing workflow with real hardware +- ECU flashing with hex files +- Communication protocol validation +- Diagnostic command testing +- Performance and stress testing + +**Status**: Framework ready, awaiting ECU specifications + +## Custom Pytest Markers + +The framework includes custom markers for test categorization and requirement traceability: + +```python +# In pytest.ini +markers = + smoke: Basic functionality tests + integration: Integration tests requiring hardware + hardware: Tests requiring physical BabyLin hardware + babylin: Tests targeting the BabyLIN SDK adapter + unit: Fast unit tests (no hardware) + boundary: Boundary condition and edge case tests + req_001: Tests validating requirement REQ-001 (LIN Interface Basic Operations) + req_002: Tests validating requirement REQ-002 (Master Request/Response) + req_003: Tests validating requirement REQ-003 (Frame Validation) + req_004: Tests validating requirement REQ-004 (Timeout Handling) +``` + +## BabyLIN Integration Details + +### SDK Python wrapper + +The framework uses the official SDK Python wrapper `BabyLIN_library.py` (placed under `vendor/`) and calls its BLC_* APIs. + +Key calls in the adapter (`ecu_framework/lin/babylin.py`): +- `BLC_getBabyLinPorts`, `BLC_openPort` — discovery and open +- `BLC_loadSDF`, `BLC_getChannelHandle`, `BLC_sendCommand('start schedule N;')` — SDF + scheduling +- `BLC_mon_set_xmit` — transmit +- `BLC_getNextFrameTimeout` — receive +- `BLC_sendRawMasterRequest` — master request (length then bytes) + +## Development Workflow + +### 1. Development Phase +```powershell +# Use mock interface for development +python -m pytest tests\test_smoke_mock.py -v +``` + +### 2. Hardware Integration Phase +```powershell +# Test with real BabyLIN hardware +python -m pytest -m "hardware and babylin" -v +``` + +### 3. Full System Testing +```powershell +# Complete test suite including ECU flashing +python -m pytest -v +``` + +## Enhanced Reporting Output Example + +The enhanced HTML report includes: + +| Result | Test | Title | Requirements | Duration | Links | +|--------|------|-------|--------------|----------|--------| +| ✅ Passed | test_mock_send_receive_echo | Mock LIN Interface - Send/Receive Echo Test | REQ-001, REQ-003 | 1 ms | | +| ✅ Passed | test_mock_request_synthesized_response | Mock LIN Interface - Master Request Response Test | REQ-002 | 0 ms | | +| ✅ Passed | test_mock_receive_timeout_behavior | Mock LIN Interface - Receive Timeout Test | REQ-004 | 106 ms | | + +## Framework Validation Results + +**Current Status**: ✅ **All core features implemented and tested** + +**Mock Interface Tests**: 7/7 passing (0.14s execution time) +- Send/receive operations: ✅ Working +- Timeout handling: ✅ Working +- Frame validation: ✅ Working +- Boundary testing: ✅ Working + +**Enhanced Reporting**: ✅ **Fully functional** +- HTML report with metadata: ✅ Working +- XML report generation: ✅ Working +- Custom pytest plugin: ✅ Working +- Docstring metadata extraction: ✅ Working + +**Configuration System**: ✅ **Complete** +- YAML configuration loading: ✅ Working +- Environment variable override: ✅ Working +- BabyLIN SDF/schedule configuration: ✅ Working + - Power supply (PSU) configuration: ✅ Working (see `config/test_config.yaml` → `power_supply`) + +## Owon Power Supply (PSU) Integration + +The framework includes a serial SCPI controller for Owon PSUs and a hardware test wired to the central config. + +- Library: `ecu_framework/power/owon_psu.py` (pyserial) +- Config: `config/test_config.yaml` (`power_supply` section) + - Optionally merge machine-specific settings from `config/owon_psu.yaml` or env `OWON_PSU_CONFIG` +- Hardware test: `tests/hardware/test_owon_psu.py` (skips unless `power_supply.enabled` and `port` present) +- quick demo: `vendor/Owon/owon_psu_quickdemo.py` + +Quick run: + +```powershell +pip install -r .\requirements.txt +copy .\config\owon_psu.example.yaml .\config\owon_psu.yaml +# edit COM port in .\config\owon_psu.yaml +pytest -k test_owon_psu_idn_and_optional_set -m hardware -q +python .\vendor\Owon\owon_psu_quick_demo.py +``` + +Common config keys: + +```yaml +power_supply: + enabled: true + port: COM4 + baudrate: 115200 + timeout: 1.0 + eol: "\n" + parity: N + stopbits: 1 + idn_substr: OWON +``` + +## Next Steps + +1. **Hardware Testing**: Connect BabyLin hardware and validate hardware smoke tests +2. **ECU Integration**: Define ECU-specific communication protocols and diagnostic commands +3. **Hex Flashing**: Implement complete hex file flashing workflow +4. **CI/CD Integration**: Set up automated testing pipeline with generated reports + +## Dependencies + +``` +pytest>=8.4.2 +pytest-html>=4.1.1 +pytest-xdist>=3.8.0 +pyyaml>=6.0.2 +``` + This framework provides a complete, production-ready testing solution for ECU development with BabyLIN communication, featuring enhanced documentation, traceability, and reporting capabilities. \ No newline at end of file diff --git a/config/babylin.example.yaml b/config/babylin.example.yaml index eb59077..8e112ff 100644 --- a/config/babylin.example.yaml +++ b/config/babylin.example.yaml @@ -1,11 +1,11 @@ -# Example configuration for BabyLIN hardware runs (SDK Python wrapper) -interface: - type: babylin - channel: 0 # Channel index (0-based) as used by the SDK - bitrate: 19200 # Usually defined by the SDF, kept for reference - node_name: ECU_TEST_NODE - sdf_path: .\vendor\Example.sdf # Path to your SDF file - schedule_nr: 0 # Schedule number to start on connect -flash: - enabled: true - hex_path: C:\\Path\\To\\firmware.hex # TODO: update +# Example configuration for BabyLIN hardware runs (SDK Python wrapper) +interface: + type: babylin + channel: 0 # Channel index (0-based) as used by the SDK + bitrate: 19200 # Usually defined by the SDF, kept for reference + node_name: ECU_TEST_NODE + sdf_path: .\vendor\Example.sdf # Path to your SDF file + schedule_nr: 0 # Schedule number to start on connect +flash: + enabled: true + hex_path: C:\\Path\\To\\firmware.hex # TODO: update diff --git a/config/examples.yaml b/config/examples.yaml index 4b93670..eff08a4 100644 --- a/config/examples.yaml +++ b/config/examples.yaml @@ -1,50 +1,50 @@ -# Examples: Mock-only and BabyLIN hardware configurations -# -# How to use (Windows PowerShell): -# # Point the framework to a specific config file -# $env:ECU_TESTS_CONFIG = ".\config\examples.yaml" -# # Run only mock tests -# pytest -m "not hardware" -v -# # Switch to the BabyLIN profile by moving it under the 'active' key or by -# # exporting a different file path containing only the desired profile. -# -# This file shows both profiles in one place; typically you'll copy the relevant -# section into its own YAML file (e.g., config/mock.yaml, config/babylin.yaml). - -# --- MOCK PROFILE ----------------------------------------------------------- -mock_profile: - interface: - type: mock - channel: 1 - bitrate: 19200 - flash: - enabled: false - hex_path: - -# --- BABYLIN PROFILE -------------------------------------------------------- -# Requires: vendor/BabyLIN_library.py and platform libraries placed per vendor/README.md -babylin_profile: - interface: - type: babylin - channel: 0 # SDK channel index (0-based) - bitrate: 19200 # Informational; SDF usually defines effective timing - node_name: ECU_TEST_NODE # Optional label - sdf_path: .\vendor\Example.sdf # Update to your real SDF path - schedule_nr: 0 # Start this schedule on connect - flash: - enabled: true - hex_path: C:\\Path\\To\\firmware.hex # Update as needed - -# --- ACTIVE SELECTION ------------------------------------------------------- -# To use one of the profiles above, copy it under the 'active' key below or -# include only that profile in a separate file. The loader expects the top-level -# keys 'interface' and 'flash' by default. For convenience, we expose a shape -# that mirrors that directly. Here is a self-contained active selection: -active: - interface: - type: mock - channel: 1 - bitrate: 19200 - flash: - enabled: false - hex_path: +# Examples: Mock-only and BabyLIN hardware configurations +# +# How to use (Windows PowerShell): +# # Point the framework to a specific config file +# $env:ECU_TESTS_CONFIG = ".\config\examples.yaml" +# # Run only mock tests +# pytest -m "not hardware" -v +# # Switch to the BabyLIN profile by moving it under the 'active' key or by +# # exporting a different file path containing only the desired profile. +# +# This file shows both profiles in one place; typically you'll copy the relevant +# section into its own YAML file (e.g., config/mock.yaml, config/babylin.yaml). + +# --- MOCK PROFILE ----------------------------------------------------------- +mock_profile: + interface: + type: mock + channel: 1 + bitrate: 19200 + flash: + enabled: false + hex_path: + +# --- BABYLIN PROFILE -------------------------------------------------------- +# Requires: vendor/BabyLIN_library.py and platform libraries placed per vendor/README.md +babylin_profile: + interface: + type: babylin + channel: 0 # SDK channel index (0-based) + bitrate: 19200 # Informational; SDF usually defines effective timing + node_name: ECU_TEST_NODE # Optional label + sdf_path: .\vendor\Example.sdf # Update to your real SDF path + schedule_nr: 0 # Start this schedule on connect + flash: + enabled: true + hex_path: C:\\Path\\To\\firmware.hex # Update as needed + +# --- ACTIVE SELECTION ------------------------------------------------------- +# To use one of the profiles above, copy it under the 'active' key below or +# include only that profile in a separate file. The loader expects the top-level +# keys 'interface' and 'flash' by default. For convenience, we expose a shape +# that mirrors that directly. Here is a self-contained active selection: +active: + interface: + type: mock + channel: 1 + bitrate: 19200 + flash: + enabled: false + hex_path: diff --git a/config/mum.example.yaml b/config/mum.example.yaml new file mode 100644 index 0000000..e09934e --- /dev/null +++ b/config/mum.example.yaml @@ -0,0 +1,29 @@ +# MUM (Melexis Universal Master) interface example. +# Copy to test_config.yaml or point ECU_TESTS_CONFIG at this file. +# +# Prerequisites: +# - MUM is reachable over IP (default 192.168.7.2 over USB-RNDIS). +# - Melexis Python packages 'pylin' and 'pymumclient' are importable. +# See vendor/automated_lin_test/install_packages.sh. + +interface: + type: mum + host: 192.168.7.2 # MUM IP address + lin_device: lin0 # MUM LIN device name + power_device: power_out0 # MUM power-control device + bitrate: 19200 # LIN baudrate + boot_settle_seconds: 0.5 # Delay after power-up before first frame + # Optional: per-frame-id data lengths. Defaults cover the 4SEVEN library + # (ALM_Status=4, ALM_Req_A=8, etc.) — only override if your ECU differs. + frame_lengths: + 0x0A: 8 # ALM_Req_A + 0x11: 4 # ALM_Status + +flash: + enabled: false + hex_path: + +# The Owon PSU is unused on the MUM flow (MUM provides power on power_out0). +# Leave disabled unless you also want to drive the Owon for a separate test. +power_supply: + enabled: false diff --git a/config/owon_psu.example.yaml b/config/owon_psu.example.yaml index 40394c5..9ef61d2 100644 --- a/config/owon_psu.example.yaml +++ b/config/owon_psu.example.yaml @@ -1,18 +1,18 @@ -# Example configuration for Owon PSU hardware test -# Copy to config/owon_psu.yaml and adjust values for your setup - -port: COM4 # e.g., COM4 on Windows, /dev/ttyUSB0 on Linux -baudrate: 115200 # default 115200 -timeout: 1.0 # seconds -# eol: "\n" # write/query line termination (default "\n"); use "\r\n" if required -# parity: N # N|E|O (default N) -# stopbits: 1 # 1 or 2 (default 1) -# xonxoff: false -# rtscts: false -# dsrdtr: false - -# Optional assertions/behavior -# idn_substr: OWON # require this substring in *IDN? -# do_set: true # briefly set V/I and toggle output -# set_voltage: 1.0 # volts when do_set is true -# set_current: 0.1 # amps when do_set is true +# Example configuration for Owon PSU hardware test +# Copy to config/owon_psu.yaml and adjust values for your setup + +port: COM4 # e.g., COM4 on Windows, /dev/ttyUSB0 on Linux +baudrate: 115200 # default 115200 +timeout: 1.0 # seconds +# eol: "\n" # write/query line termination (default "\n"); use "\r\n" if required +# parity: N # N|E|O (default N) +# stopbits: 1 # 1 or 2 (default 1) +# xonxoff: false +# rtscts: false +# dsrdtr: false + +# Optional assertions/behavior +# idn_substr: OWON # require this substring in *IDN? +# do_set: true # briefly set V/I and toggle output +# set_voltage: 1.0 # volts when do_set is true +# set_current: 0.1 # amps when do_set is true diff --git a/config/owon_psu.yaml b/config/owon_psu.yaml index fd9e641..55a7d1b 100644 --- a/config/owon_psu.yaml +++ b/config/owon_psu.yaml @@ -1,18 +1,18 @@ -# Example configuration for Owon PSU hardware test -# Copy to config/owon_psu.yaml and adjust values for your setup - -port: COM4 # e.g., COM4 on Windows, /dev/ttyUSB0 on Linux -baudrate: 115200 # default 115200 -timeout: 1.0 # seconds -eol: "\n" # write/query line termination (default "\n"); use "\r\n" if required -parity: N # N|E|O (default N) -stopbits: 1 # 1 or 2 (default 1) -xonxoff: false -rtscts: false -dsrdtr: false - -# Optional assertions/behavior -idn_substr: OWON # require this substring in *IDN? -do_set: true # briefly set V/I and toggle output -set_voltage: 10.0 # volts when do_set is true -set_current: 0.1 # amps when do_set is true +# Example configuration for Owon PSU hardware test +# Copy to config/owon_psu.yaml and adjust values for your setup + +port: COM4 # e.g., COM4 on Windows, /dev/ttyUSB0 on Linux +baudrate: 115200 # default 115200 +timeout: 1.0 # seconds +eol: "\n" # write/query line termination (default "\n"); use "\r\n" if required +parity: N # N|E|O (default N) +stopbits: 1 # 1 or 2 (default 1) +xonxoff: false +rtscts: false +dsrdtr: false + +# Optional assertions/behavior +idn_substr: OWON # require this substring in *IDN? +do_set: true # briefly set V/I and toggle output +set_voltage: 13.0 # volts when do_set is true +set_current: 1.0 # amps when do_set is true (raise above ECU draw to stay in CV mode) diff --git a/config/test_config.yaml b/config/test_config.yaml index 1421df8..9b8b53c 100644 --- a/config/test_config.yaml +++ b/config/test_config.yaml @@ -1,18 +1,34 @@ interface: - type: mock - channel: 1 - bitrate: 19200 + # MUM (Melexis Universal Master) is the current default. Switch type to + # 'babylin' for the legacy SDK flow, or 'mock' for hardware-free runs. + 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 (master-published, RGB control) + 0x11: 4 # ALM_Status (slave-published) + + # --- BabyLIN (legacy) settings, used only when type: babylin --- + channel: 0 + node_name: ECU_TEST_NODE + sdf_path: .\vendor\4SEVEN_color_lib_test.sdf + schedule_nr: -1 # -1 = don't auto-start a schedule + flash: enabled: false hex_path: -# Optional: central power supply config used by hardware tests/demos -# You can also place machine-specific values in config/owon_psu.yaml or set OWON_PSU_CONFIG +# Owon PSU is independent of the LIN interface. The MUM provides its own +# power on power_out0, so leave the PSU disabled unless you specifically +# need to drive an external supply for over/under-voltage scenarios. power_supply: - enabled: true + enabled: false # port: COM4 baudrate: 115200 - timeout: 1.0 + timeout: 2.0 eol: "\n" parity: N stopbits: 1 @@ -21,5 +37,5 @@ power_supply: dsrdtr: false # idn_substr: OWON do_set: false - set_voltage: 1.0 - set_current: 0.1 + set_voltage: 13.0 + set_current: 1.0 diff --git a/conftest.py b/conftest.py index 73834fd..343a122 100644 --- a/conftest.py +++ b/conftest.py @@ -1,27 +1,27 @@ -""" -Pytest configuration for this repository. - -Purpose: -- Optionally register the local plugin in `conftest_plugin.py` if present. -- Avoid hard failures on environments where that file isn't available. -""" -from __future__ import annotations - -import importlib -import sys -from typing import Any - - -def pytest_configure(config: Any) -> None: - try: - plugin = importlib.import_module("conftest_plugin") - except Exception as e: - # Soft warning only; tests can still run without the extra report features. - sys.stderr.write(f"[pytest] conftest_plugin not loaded: {e}\n") - return - - # Register the plugin module so its hooks are active. - try: - config.pluginmanager.register(plugin, name="conftest_plugin") - except Exception as reg_err: - sys.stderr.write(f"[pytest] failed to register conftest_plugin: {reg_err}\n") +""" +Pytest configuration for this repository. + +Purpose: +- Optionally register the local plugin in `conftest_plugin.py` if present. +- Avoid hard failures on environments where that file isn't available. +""" +from __future__ import annotations + +import importlib +import sys +from typing import Any + + +def pytest_configure(config: Any) -> None: + try: + plugin = importlib.import_module("conftest_plugin") + except Exception as e: + # Soft warning only; tests can still run without the extra report features. + sys.stderr.write(f"[pytest] conftest_plugin not loaded: {e}\n") + return + + # Register the plugin module so its hooks are active. + try: + config.pluginmanager.register(plugin, name="conftest_plugin") + except Exception as reg_err: + sys.stderr.write(f"[pytest] failed to register conftest_plugin: {reg_err}\n") diff --git a/conftest_plugin.py b/conftest_plugin.py index 7bd45ea..c11f8f6 100644 --- a/conftest_plugin.py +++ b/conftest_plugin.py @@ -1,261 +1,261 @@ -""" -Custom pytest plugin to enhance test reports with detailed metadata. - -Why we need this plugin: -- Surface business-facing info (Title, Description, Requirements, Steps, Expected Result) in the HTML report for quick review. -- Map tests to requirement IDs and produce a requirements coverage JSON artifact for traceability. -- Emit a compact CI summary (summary.md) for dashboards and PR comments. - -How it works (high level): -- During collection, we track all test nodeids for later "unmapped" reporting. -- During test execution, we parse the test function's docstring and markers to extract metadata and requirement IDs; we attach these as user_properties on the report. -- We add custom columns (Title, Requirements) to the HTML table. -- At the end of the run, we write two artifacts into reports/: requirements_coverage.json and summary.md. -""" - -import os -import re -import json -import datetime as _dt -import pytest - -# ----------------------------- -# Session-scoped state for reports -# ----------------------------- -# Track all collected tests (nodeids) so we can later highlight tests that had no requirement mapping. -_ALL_COLLECTED_TESTS: set[str] = set() -# Map requirement ID (e.g., REQ-001) -> set of nodeids that cover it. -_REQ_TO_TESTS: dict[str, set[str]] = {} -# Nodeids that did map to at least one requirement. -_MAPPED_TESTS: set[str] = set() - - -def _normalize_req_id(token: str) -> str | None: - """Normalize requirement token to REQ-XXX form. - - Accepts markers like 'req_001' or strings like 'REQ-001'. - Returns None if not a recognizable requirement. This provides a single - canonical format for coverage mapping and reporting. - """ - token = token.strip() - m1 = re.fullmatch(r"req_(\d{1,3})", token, re.IGNORECASE) - if m1: - return f"REQ-{int(m1.group(1)):03d}" - m2 = re.fullmatch(r"REQ[-_ ]?(\d{1,3})", token, re.IGNORECASE) - if m2: - return f"REQ-{int(m2.group(1)):03d}" - return None - - -def _extract_req_ids_from_docstring(docstring: str) -> list[str]: - """Parse the 'Requirements:' line in the docstring and return REQ-XXX tokens. - - Supports comma- or whitespace-separated tokens and normalizes them. - """ - reqs: list[str] = [] - req_match = re.search(r"Requirements:\s*(.+)", docstring) - if req_match: - raw = req_match.group(1) - # split by comma or whitespace - parts = re.split(r"[\s,]+", raw) - for p in parts: - rid = _normalize_req_id(p) - if rid: - reqs.append(rid) - return list(dict.fromkeys(reqs)) # dedupe, preserve order - - -def pytest_configure(config): - # Ensure reports directory exists early so downstream hooks can write artifacts safely - os.makedirs("reports", exist_ok=True) - - -def pytest_collection_modifyitems(session, config, items): - # Track all collected tests for unmapped detection (for the final coverage JSON) - for item in items: - _ALL_COLLECTED_TESTS.add(item.nodeid) - - -# (Legacy makereport implementation removed in favor of the hookwrapper below.) - - -def pytest_html_results_table_header(cells): - """Add custom columns to HTML report table. - - Why: Make the most important context (Title and Requirements) visible at a glance - in the HTML report table without opening each test details section. - """ - cells.insert(2, '