refactor: retire LIN API generator (move to deprecated/)
With AlmTester now the single contributor-facing API, the generator at
``scripts/gen_lin_api.py`` and its output at
``tests/hardware/_generated/`` have no live consumer — the previous
commit inlined the enum classes they used to provide into
``tests/hardware/alm_helpers.py``.
Moves both to ``deprecated/`` rather than deleting outright. The
deprecated layout is self-describing:
deprecated/
README.md — retirement rationale + revival instructions
gen_lin_api.py — was scripts/gen_lin_api.py
_generated/
__init__.py
lin_api.py — last-emitted typed frame classes + IntEnums
A note in deprecated/README.md spells out the conditions that would
make reviving the generator worthwhile (a second ECU joins, the LDF
churns fast enough to make hand-syncing miss changes, mypy-in-CI gets
adopted) and the exact command to regenerate.
Docs:
- 22_generated_lin_api.md now leads with a retired-layer banner. The
body is preserved as the design-of-record for the historical layer.
- 05_architecture_overview.md gets a refreshed "Test-side layering"
Mermaid (AlmTester → FrameIO → LinInterface) plus a "retired layer"
bullet pointing at deprecated/. The "Three independent entry points"
section is annotated rather than removed — the gen_lin_api path
there is now historical reference.
Verified: pytest --collect-only collects 87 tests; 40 unit + mock
tests still pass. The retirement is invisible to the live framework.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
08247f9321
commit
90be834102
36
deprecated/README.md
Normal file
36
deprecated/README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Deprecated
|
||||||
|
|
||||||
|
Historical artifacts kept for reference. Nothing in this directory is
|
||||||
|
imported by the live framework or test suite.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
| Path | Was | Why retired |
|
||||||
|
|---|---|---|
|
||||||
|
| `gen_lin_api.py` | `scripts/gen_lin_api.py` — LDF → Python generator | Replaced by hand-maintained `AlmTester` in `tests/hardware/alm_helpers.py`. The generated layer had one consumer (`AlmTester`) and adding signals to the generator was less ergonomic than adding methods to the facade. |
|
||||||
|
| `_generated/lin_api.py` | `tests/hardware/_generated/lin_api.py` — auto-emitted typed frame classes + enum classes | Same — its enum classes were inlined into `alm_helpers.py` so test bodies need only one import. |
|
||||||
|
|
||||||
|
## If you want to bring this back
|
||||||
|
|
||||||
|
The design is documented in
|
||||||
|
[`../docs/22_generated_lin_api.md`](../docs/22_generated_lin_api.md).
|
||||||
|
Reasons it might be worth reviving:
|
||||||
|
|
||||||
|
- A second ECU joins the framework and adding `<ecu>_helpers.py`
|
||||||
|
facades by hand becomes painful.
|
||||||
|
- The LDF starts churning fast enough that hand-syncing
|
||||||
|
`alm_helpers.py` enums against it is missing changes.
|
||||||
|
- The team grows to where mypy-in-CI becomes worth it and the
|
||||||
|
generated dataclass shape (with typed signal attributes) starts
|
||||||
|
paying for itself.
|
||||||
|
|
||||||
|
The generator is self-contained and parses a single LDF — re-runnable
|
||||||
|
in place from this directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
python deprecated/gen_lin_api.py vendor/4SEVEN_color_lib_test.ldf \
|
||||||
|
--out tests/hardware/_generated/lin_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
(Restoring also requires adding `tests/hardware/_generated/__init__.py`
|
||||||
|
back, which is currently inside this folder.)
|
||||||
@ -24,9 +24,9 @@ This document provides a high-level view of the framework’s components and how
|
|||||||
- Project-wide fixtures — `tests/conftest.py` (config, lin, ldf, flash_ecu, rp)
|
- Project-wide fixtures — `tests/conftest.py` (config, lin, ldf, flash_ecu, rp)
|
||||||
- Hardware-suite fixtures — `tests/hardware/conftest.py` (session-scoped, autouse PSU; the bench is powered up once at session start and stays on for every test in the suite)
|
- Hardware-suite fixtures — `tests/hardware/conftest.py` (session-scoped, autouse PSU; the bench is powered up once at session start and stays on for every test in the suite)
|
||||||
- MUM-suite fixtures — `tests/hardware/mum/conftest.py` (session-scoped `fio`, `nad`, `alm`; autouse `_require_mum` gate and `_reset_to_off` per-test reset). Tests outside `tests/hardware/mum/` cannot see these — that's how PSU-only and BabyLIN-only tests are kept from accidentally requesting MUM fixtures.
|
- MUM-suite fixtures — `tests/hardware/mum/conftest.py` (session-scoped `fio`, `nad`, `alm`; autouse `_require_mum` gate and `_reset_to_off` per-test reset). Tests outside `tests/hardware/mum/` cannot see these — that's how PSU-only and BabyLIN-only tests are kept from accidentally requesting MUM fixtures.
|
||||||
- Generic LDF I/O — `tests/hardware/frame_io.py` (`FrameIO` — send/receive/pack/unpack for any LDF frame plus raw-bus escape hatches). Stringly-typed at this layer (`fio.send("ALM_Req_A", …)`); typed wrappers live one level up.
|
- Generic LDF I/O — `tests/hardware/frame_io.py` (`FrameIO` — send/receive/pack/unpack for any LDF frame plus raw-bus escape hatches). Stringly-typed at this layer (`fio.send("ALM_Req_A", …)`); tests use this **only** for cases the AlmTester facade doesn't model (schema introspection, raw-frame escape hatches, MUM-only `send_raw`).
|
||||||
- Generated LIN API — `tests/hardware/_generated/lin_api.py` (auto-emitted from an LDF by `scripts/gen_lin_api.py`; one class per frame, one `IntEnum` per encoding type with logical values). **Build-time, static.** Provides typed names so frame/signal typos become import errors. Design + generation rules in `docs/22_generated_lin_api.md`; relationship to `ecu_framework/lin/ldf.py` covered in [LDF Database vs Generated LIN API](#ldf-database-vs-generated-lin-api-two-layers-one-purpose).
|
- ALM domain helpers — `tests/hardware/alm_helpers.py` (`AlmTester` + hand-maintained `IntEnum` classes). The **single contributor-facing API** for ALM tests: per-signal readers (`read_led_state`, `read_voltage_status`, …), per-action senders (`send_color`, `send_config`, …), wait helpers, and cross-frame patterns (`assert_pwm_matches_rgb`). See [`19_frame_io_and_alm_helpers.md`](19_frame_io_and_alm_helpers.md).
|
||||||
- ALM domain helpers — `tests/hardware/alm_helpers.py` (`AlmTester` — force_off / wait_for_state / measure_animating_window / assert_pwm_*). Imports typed frames + enums from the generated layer; keeps the non-generatable semantics (polling cadences, PWM tolerances, cross-frame test patterns).
|
- Retired layer (kept under [`deprecated/`](../deprecated/)) — `deprecated/gen_lin_api.py` + `deprecated/_generated/lin_api.py`. Was an LDF→Python generator that emitted typed frame/encoding classes; replaced by the hand-maintained surface in `alm_helpers.py`. See [`22_generated_lin_api.md`](22_generated_lin_api.md) for the historical design.
|
||||||
- PSU settle helpers — `tests/hardware/psu_helpers.py` (`wait_until_settled`, `apply_voltage_and_settle` — measured-rail-then-validation pattern shared by all voltage-changing tests)
|
- PSU settle helpers — `tests/hardware/psu_helpers.py` (`wait_until_settled`, `apply_voltage_and_settle` — measured-rail-then-validation pattern shared by all voltage-changing tests)
|
||||||
- RGB→PWM calculator — `vendor/rgb_to_pwm.py` (consumed by `AlmTester.assert_pwm_*`)
|
- RGB→PWM calculator — `vendor/rgb_to_pwm.py` (consumed by `AlmTester.assert_pwm_*`)
|
||||||
- Test templates (not collected) — `tests/hardware/_test_case_template.py`, `tests/hardware/_test_case_template_psu_lin.py`
|
- Test templates (not collected) — `tests/hardware/_test_case_template.py`, `tests/hardware/_test_case_template_psu_lin.py`
|
||||||
@ -49,15 +49,15 @@ flowchart TB
|
|||||||
end
|
end
|
||||||
|
|
||||||
subgraph Hardware_Helpers [Hardware-test helpers]
|
subgraph Hardware_Helpers [Hardware-test helpers]
|
||||||
FIO[tests/hardware/frame_io.py<br/>FrameIO (stringly-typed)]
|
ALM[tests/hardware/alm_helpers.py<br/>AlmTester + typed IntEnums<br/>(contributor-facing API)]
|
||||||
GEN[tests/hardware/_generated/lin_api.py<br/>AlmReqA, AlmStatus, ... (typed)<br/>LedState, Mode, Update IntEnums]
|
FIO[tests/hardware/frame_io.py<br/>FrameIO (low-level, rarely used by tests)]
|
||||||
ALM[tests/hardware/alm_helpers.py<br/>AlmTester]
|
|
||||||
RGB[vendor/rgb_to_pwm.py]
|
RGB[vendor/rgb_to_pwm.py]
|
||||||
TPL[tests/hardware/_test_case_template*.py<br/>not collected]
|
TPL[tests/hardware/_test_case_template*.py<br/>not collected]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Build_Time [Build-time tooling (not run during tests)]
|
subgraph Retired [Retired (deprecated/)]
|
||||||
GENSCRIPT[scripts/gen_lin_api.py]
|
GEN[deprecated/_generated/lin_api.py]
|
||||||
|
GENSCRIPT[deprecated/gen_lin_api.py]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Framework
|
subgraph Framework
|
||||||
@ -94,14 +94,11 @@ flowchart TB
|
|||||||
CF --> BABY
|
CF --> BABY
|
||||||
CF --> FLASH
|
CF --> FLASH
|
||||||
HCF --> POWER
|
HCF --> POWER
|
||||||
T --> FIO
|
|
||||||
T --> GEN
|
|
||||||
T --> ALM
|
T --> ALM
|
||||||
|
T -.rare, low-level.-> FIO
|
||||||
ALM --> FIO
|
ALM --> FIO
|
||||||
ALM --> GEN
|
GENSCRIPT -.was: read LDF.-> LDFFILE
|
||||||
GEN -.calls at runtime.-> FIO
|
GENSCRIPT -.was: emit source.-> GEN
|
||||||
GENSCRIPT -.reads LDF once.-> LDFFILE
|
|
||||||
GENSCRIPT -.emits source.-> GEN
|
|
||||||
ALM --> RGB
|
ALM --> RGB
|
||||||
TPL -.copy & edit.-> T
|
TPL -.copy & edit.-> T
|
||||||
|
|
||||||
@ -146,6 +143,15 @@ flowchart TB
|
|||||||
|
|
||||||
## LDF Database vs Generated LIN API: two layers, one purpose
|
## LDF Database vs Generated LIN API: two layers, one purpose
|
||||||
|
|
||||||
|
> **Historical.** The generated LIN API is retired — see the banner in
|
||||||
|
> [`22_generated_lin_api.md`](22_generated_lin_api.md). The comparison
|
||||||
|
> below is kept for traceability: the *runtime* `LdfDatabase` path is
|
||||||
|
> still active (it's what `FrameIO` calls into); the *generated* path
|
||||||
|
> column describes a code path that now lives under
|
||||||
|
> [`deprecated/`](../deprecated/). Today, the analog of the right
|
||||||
|
> column is the hand-maintained `IntEnum` classes and method surface
|
||||||
|
> in `tests/hardware/alm_helpers.py`.
|
||||||
|
|
||||||
There are two pieces of code in this repo whose names both sound like
|
There are two pieces of code in this repo whose names both sound like
|
||||||
"the LDF module", and a recurring question is why both exist:
|
"the LDF module", and a recurring question is why both exist:
|
||||||
|
|
||||||
@ -181,14 +187,20 @@ have no path from a frame name to the actual byte layout for the currently
|
|||||||
loaded LDF — and worse, the test bench would happily ship bytes encoded
|
loaded LDF — and worse, the test bench would happily ship bytes encoded
|
||||||
against a *stale* LDF baked into the generator's last run.
|
against a *stale* LDF baked into the generator's last run.
|
||||||
|
|
||||||
### Three independent entry points, one wire
|
### Test-side entry points
|
||||||
|
|
||||||
A tester has three legitimate ways to drive the bus, all converging at
|
> **Updated.** This section originally described three parallel paths
|
||||||
`LinInterface`. They are **parallel paths**, not a single nested stack —
|
> including the generated typed-wrapper layer. With the generator
|
||||||
`FrameIO` deliberately has no static dependency on `ecu_framework/lin/ldf.py`
|
> retired, the active picture is simpler: tests reach for `AlmTester`
|
||||||
(its only `ecu_framework` import is `LinInterface` + `LinFrame` from
|
> by default, and drop down to `FrameIO` only for schema introspection
|
||||||
`lin/base.py`), so the `ldf` it receives can be any object with a
|
> or other low-level needs. The diagram below is preserved for
|
||||||
`.frame(name)` method.
|
> reference but the `gen_lin_api` node represents the now-retired
|
||||||
|
> path — see [`deprecated/`](../deprecated/).
|
||||||
|
|
||||||
|
`FrameIO` deliberately has no static dependency on
|
||||||
|
`ecu_framework/lin/ldf.py` (its only `ecu_framework` import is
|
||||||
|
`LinInterface` + `LinFrame` from `lin/base.py`), so the `ldf` it
|
||||||
|
receives can be any object with a `.frame(name)` method.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@ -395,13 +407,13 @@ slot is where the team chose to keep things light.
|
|||||||
|
|
||||||
- Add new bus adapters by implementing `LinInterface`
|
- Add new bus adapters by implementing `LinInterface`
|
||||||
- Add new ECU-domain helpers next to `AlmTester` (e.g. `BcmTester`)
|
- Add new ECU-domain helpers next to `AlmTester` (e.g. `BcmTester`)
|
||||||
on top of `FrameIO` and the generated `lin_api.py`; share fixtures via
|
on top of `FrameIO`; share fixtures via
|
||||||
`tests/hardware/conftest.py`
|
`tests/hardware/conftest.py` (or a per-adapter conftest like
|
||||||
|
`tests/hardware/mum/conftest.py`)
|
||||||
- When the LDF changes (new frame, renamed signal, new encoding-type row):
|
- When the LDF changes (new frame, renamed signal, new encoding-type row):
|
||||||
re-run `python scripts/gen_lin_api.py <ldf-path>`, commit the updated
|
add or update the corresponding `read_*` / `send_*` method (and, if
|
||||||
`tests/hardware/_generated/lin_api.py` alongside the LDF change. The
|
needed, a new `IntEnum`) in `tests/hardware/alm_helpers.py`. This is
|
||||||
in-sync unit test in `tests/unit/test_generated_lin_api_in_sync.py`
|
the maintenance pact that replaced the retired generator
|
||||||
fails CI if the two ever drift
|
|
||||||
- Add new bench instrument controllers next to `OwonPSU` under
|
- Add new bench instrument controllers next to `OwonPSU` under
|
||||||
`ecu_framework/power/` or a new `ecu_framework/instruments/` package,
|
`ecu_framework/power/` or a new `ecu_framework/instruments/` package,
|
||||||
expose them as session-scoped fixtures
|
expose them as session-scoped fixtures
|
||||||
|
|||||||
@ -1,14 +1,33 @@
|
|||||||
# Generated LIN API: One Helper per Frame, Enums per Encoding Type
|
# Generated LIN API: One Helper per Frame, Enums per Encoding Type
|
||||||
|
|
||||||
|
> # ⚠ Retired layer — historical reference only
|
||||||
|
>
|
||||||
|
> The generator described here was retired when `AlmTester`
|
||||||
|
> (`tests/hardware/alm_helpers.py`) became the single contributor-facing
|
||||||
|
> surface. The relevant `IntEnum` classes (`LedState`, `Mode`, `Update`,
|
||||||
|
> `NVMStatus`, `VoltageStatus`, `ThermalStatus`) are now defined directly
|
||||||
|
> in `alm_helpers.py` and updated by hand when the LDF changes. The
|
||||||
|
> generator script and its last-emitted output live under
|
||||||
|
> [`../deprecated/`](../deprecated/) for reference; see
|
||||||
|
> [`../deprecated/README.md`](../deprecated/README.md) for the retirement
|
||||||
|
> rationale and the conditions under which reviving it would be worth it.
|
||||||
|
>
|
||||||
|
> Test bodies should reach for `AlmTester` methods (`alm.send_color`,
|
||||||
|
> `alm.read_led_state`, etc.) and the enums it exposes — see
|
||||||
|
> [`19_frame_io_and_alm_helpers.md`](19_frame_io_and_alm_helpers.md) for
|
||||||
|
> the active API.
|
||||||
|
>
|
||||||
|
> Everything below this banner describes the **previous** design, kept
|
||||||
|
> for traceability. Paths reference the original locations
|
||||||
|
> (`scripts/gen_lin_api.py`, `tests/hardware/_generated/lin_api.py`) —
|
||||||
|
> both files now live under `deprecated/`.
|
||||||
|
|
||||||
This document describes the design for `tests/hardware/_generated/lin_api.py`,
|
This document describes the design for `tests/hardware/_generated/lin_api.py`,
|
||||||
a file produced by `scripts/gen_lin_api.py` from an LDF. The goal is to push
|
a file produced by `scripts/gen_lin_api.py` from an LDF. The goal is to push
|
||||||
every frame/signal/encoding-type fact out of hand-written test code and into a
|
every frame/signal/encoding-type fact out of hand-written test code and into a
|
||||||
single regenerated module that tests, helpers, and future ECU domains can
|
single regenerated module that tests, helpers, and future ECU domains can
|
||||||
import from.
|
import from.
|
||||||
|
|
||||||
Nothing in this document has been committed yet — it is the design that the
|
|
||||||
generator will follow once approved.
|
|
||||||
|
|
||||||
## Why have a generated layer at all
|
## Why have a generated layer at all
|
||||||
|
|
||||||
`tests/hardware/frame_io.py` is already domain-agnostic: it takes a frame
|
`tests/hardware/frame_io.py` is already domain-agnostic: it takes a frame
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user