# Fi-Wi framework — authoring test cases and automation This document is for people who write **repeatable checks**, **calibration procedures**, or **scripts** around Fi-Wi—not for end users who only run `panel calibrate` interactively. For shell commands, see [fiwi-cli.md](fiwi-cli.md). For code structure, see [fiwi-design.md](fiwi-design.md). The repository does not yet ship a pytest suite for `fiwi/`; the patterns below apply whether you drive **`subprocess`** against `fiwi.py` or import **`fiwi`** as a library. ## Choose a control style 1. **Shell / subprocess** — Easiest to run exactly what operators run. Parse JSON from stdout for machine-readable commands (`calibrate-ports-json`, `lsusb-lines-json`, `wlan-info-json`). Stable for CI-style wrappers. 2. **Python imports** — Use `FiWiHarness`, `SshNode`, `FiberRadioPort`, and `fiwi.diag_log` for tighter integration, error handling, and async (`ainvoke_capture`, `alog_dmesg`, and so on). Mix both: for example import `SshNode` to run remote JSON probes while keeping local steps in the shell. ## Stable machine-readable commands These are intended for automation (same argv on local or remote via `--ssh`): | Command | Typical use | |---------|-------------| | `calibrate-ports-json` | Ordered `[[hub, port], …]` for calibration or power walks. | | `lsusb-lines-json` | USB topology snapshot on the host where the command runs. | | `wlan-info-json` | Wireless NIC metadata for map fill-in over SSH. | | `status` | Human tables; less ideal for parsing than JSON helpers. | When testing **remote** behavior, run the same subcommand with `fiwi.py --ssh user@host …` so the environment matches production. ## Fiber map as test fixture - Keep a **golden** `fiber_map.json` (or merge from `fiber_map.example.json`) under version control for non-interactive tests. - For SSH-mapped fibers, ensure the map on the **workstation** points at the DUT, and the map on the **DUT** marks those ports as local to avoid forwarding loops. - Use **`power fiber-port on|off`** in scripts when the goal is “same as operator” routing through the map. ## Deferred remote work (overlap) If your automation fires **many SSH captures** in parallel (similar to `panel calibrate`): - Set **`FIWI_REMOTE_DEFER=1`** or pass **`--async`** so deferred calls return **`RemoteCallHandle`** or **`asyncio.Task`** instead of blocking immediately inside each call. - Join with **`.result()`** on handles or **`await`** on tasks before asserting outcomes. Avoid assuming synchronous blocking remote calls if defer is enabled. ## Async library example (sketch) ```python import asyncio from fiwi import SshNode, alog_hardware_snapshot, get_diag_log async def remote_baseline(host: str): log = get_diag_log() ssh = SshNode.parse(host) await alog_hardware_snapshot(log, ssh=ssh, caption=f"baseline {host}") code, out, err = await ssh.ainvoke_capture(["wlan-info-json"], timeout=60) assert code == 0, (code, err) # parse JSON from `out` … asyncio.run(remote_baseline("pi@192.168.1.50")) ``` Use **`ainvoke_capture`** / **`araw_ssh`** when you are already inside an async test harness; use sync **`invoke_capture`** / **`raw_ssh`** for straight-line scripts. ## Diagnostic log in tests - **`get_diag_log()`** returns a process-wide **`DiagLog`**; call **`log.clear()`** at the start of a test case if you need isolation without subprocess boundaries. - **`alog_hardware_snapshot`** appends note + **dmesg** + **lspci** in a fixed order (captures may run concurrently, but log order is stable). - **`KernelDumpEvent`** is reserved for future kdump/vmcore steps; today you can append **`kernel_dump_event(...)`** manually to exercise JSONL export. **`dump_jsonl(path)`** writes one JSON object per line suitable for attaching to bug reports. ## Good practices - **Timeouts**: Always pass explicit timeouts on remote calls that your test framework allows to fail slowly. - **Idempotence**: Prefer power-off at the end of destructive sequences; document required starting hub state. - **Secrets**: Do not commit `remote_ssh.env` with passwords; prefer keys and `FIWI_SSH_OPTS`. - **Assertions**: When checking JSON from `invoke_capture`, assert **exit code** and parse errors separately so failures show stderr from the remote `fiwi.py`. ## Where to add real unit tests later A future **`tests/`** package can import **`fiwi`** modules with BrainStem mocked or skipped, and subprocess tests can point `fiwi.paths.configure` at a temporary directory with a throwaway `fiber_map.json`. This document stays valid: same JSON commands and public API surface.