openscribe/docs/testing.md
Laurence f84bbca80b
Some checks failed
ci / openapi (pull_request) Failing after 30s
ci / firmware (pull_request) Failing after 35s
ci / server (pull_request) Failing after 28s
ci / emulator (pull_request) Failing after 36s
test(dev): emulation + linting harness (Wokwi, native tests, CI, docs)
Answers "how do we emulate/lint this?" with runnable tooling for every part, and
factors the pure firmware logic out so it is unit-testable on the host.

What changed:
- firmware/src/wav.h, firmware/src/httprange.h: pure, header-only WAV header builder
  and HTTP Range parser (no Arduino deps), so the tricky byte-layout and range logic
  can be tested on a PC. storage.cpp and api_http.cpp refactored to use them.
- firmware/test/test_pure/: Unity tests for the WAV header fields and Range parsing
  (start-end, open-ended, clamped, unsatisfiable, non-bytes). Run: pio test -e native.
- firmware/platformio.ini: add [env:native] (host tests) and [env:esp32s3_wokwi]
  (emulator build with default WiFi = Wokwi-GUEST so the API comes up in the sim).
- firmware/wokwi.toml + firmware/diagram.json: Wokwi emulator harness (ESP32-S3 +
  microSD + button + LED). Note: Wokwi has no I2S mic part, so audio isn't emulated;
  the harness targets boot + WiFi + REST API.
- firmware/src/main.cpp + config.cpp: bring up WiFi + API even if audio/SD init fails
  (device stays reachable, reports the fault via GET /device); compile-time default
  WiFi honoured when NVS is empty (used by the Wokwi build).
- .forgejo/workflows/ci.yml: add native tests + cppcheck to the firmware job; new
  openapi job (openapi-spec-validator) and emulator job (Wokwi build, plus a full run
  when WOKWI_CLI_TOKEN is set).
- docs/testing.md: the full emulate/lint guide for firmware, server, API, app, case.
- state/: NOTES points at the guide; TODO reflects this branch.

Why:
- The firmware can't be flashed yet (no parts) and doesn't build on this dev host, so
  we need host-runnable checks. Pure-logic unit tests + OpenAPI validation run anywhere;
  Wokwi emulates boot/WiFi/API; CI compiles the real firmware. Verified locally: the
  OpenAPI spec validates (12 paths, 10 schemas).

Notes:
- Native tests and cppcheck run in CI (no compiler on the dev host). The Wokwi full run
  is skipped unless a WOKWI_CLI_TOKEN secret is present; the build is still verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 12:16:25 +01:00

4.3 KiB

Emulating and linting OpenScribe

How to check each part of the project without (and with) the physical device. Everything here also runs in CI (.forgejo/workflows/ci.yml).

TL;DR

Part Lint / static check Compile / build Emulate / run
Firmware pio check (cppcheck) pio run -e esp32s3_dev Wokwi (pio run -e esp32s3_wokwi + wokwi-cli)
Firmware logic - pio test -e native native unit tests (host)
Server ruff check app - uvicorn app.main:app + hit /docs
API spec openapi-spec-validator - Swagger UI at /docs
App flutter analyze flutter build flutter run / emulator
Case - openscad -o out.stl ... OpenSCAD preview (F5)

Firmware (ESP32-S3)

You do not need the board to catch most problems.

1. Compile-check (the strongest lint for C++)

Install PlatformIO once (pip install platformio), then from firmware/:

pio run -e esp32s3_dev      # dev board profile
pio run -e xiao_esp32s3     # compact profile

A clean build type-checks every file and every library call. This is what CI does.

2. Static analysis

pio check -e esp32s3_dev --fail-on-defect high --skip-packages

Runs cppcheck across the sources. Add clang-tidy locally with --tool clangtidy if installed.

3. Native unit tests (no hardware, fast)

The hardware-independent logic is factored into header-only files (src/wav.h, src/httprange.h) so it compiles and runs on your PC:

pio test -e native

This builds and runs test/test_pure/ against desktop GCC and checks the WAV header byte layout and the HTTP Range parser. No board, no toolchain download for Xtensa.

4. Emulate the running firmware with Wokwi

Wokwi simulates the ESP32-S3 including virtual WiFi, so you can boot the firmware, watch the serial log, and reach the REST API without hardware.

Build the emulator profile (its WiFi defaults to the open Wokwi-GUEST network so the API comes up):

pio run -e esp32s3_wokwi

Then either:

  • VS Code: install the "Wokwi Simulator" extension, open firmware/, press play. It reads wokwi.toml + diagram.json.
  • CLI / CI: with a free WOKWI_CLI_TOKEN, wokwi-cli --timeout 15000 --expect-text "API at http" firmware.

Wokwi limitations to know: there is no I2S MEMS microphone part, so audio capture is not emulated (the mic reads nothing); microSD support is basic. The harness is for boot, WiFi, and the REST API surface, which is exactly what the emulator profile targets. Because the firmware brings the API up even when audio/SD init fails, GET /api/v1/device is reachable in the sim and reports the fault.

Server (FastAPI)

From server/:

python -m venv .venv && . .venv/Scripts/activate   # or .venv/bin/activate
pip install -r requirements.txt
ruff check app                 # lint
uvicorn app.main:app --reload  # run; browse http://localhost:8000/docs
pytest                          # tests (added as endpoints gain logic in M5+)

GET /health and the stubbed recording routes are live now; the AI pipeline arrives in M5/M6.

API spec (OpenAPI)

api/openapi.yaml is the contract both the device and server implement. Validate it:

pip install openapi-spec-validator pyyaml
python -c "from openapi_spec_validator import validate; import yaml; \
  validate(yaml.safe_load(open('api/openapi.yaml'))); print('OpenAPI valid')"

Browse it interactively by running the server (/docs) or pasting the file into https://editor.swagger.io.

App (Flutter, from M7)

Once the app project exists:

cd app
flutter pub get
flutter analyze     # lint + type-check
flutter test        # widget/unit tests
flutter run         # on an Android/iOS emulator or device

Case (OpenSCAD)

openscad -o case_top.stl -D 'part="top"' case/openscribe_case.scad

openscad --check-parameters and a headless render catch syntax/geometry errors; the GUI preview (F5) is the visual check.

What CI runs

.forgejo/workflows/ci.yml on the self-hosted runner: firmware build (both profiles) + native tests + cppcheck; OpenAPI validation; a Wokwi build (and a full emulator run if WOKWI_CLI_TOKEN is set); server lint + import check.