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>
132 lines
4.3 KiB
Markdown
132 lines
4.3 KiB
Markdown
<!-- SPDX-License-Identifier: CC-BY-SA-4.0 -->
|
|
# 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/`:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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](https://wokwi.com) 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):
|
|
|
|
```bash
|
|
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/`:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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)
|
|
|
|
```bash
|
|
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.
|