A pretend ESP-IDF, so the shared voice loop can be tested at all

components/voice/src/voice_loop.c is the one program every board runs, and until now it was in no host build. Its intent mapping — the thing that decides whether a press becomes a call — was verified only by diffing it against the four device files it replaced, and the bug that cost an afternoon (a remote press latched and never read) lived exactly there.

The loop is ESP-IDF-coupled by design: it owns FreeRTOS tasks, queues, the task watchdog and the platform transport, because those are the parts a device actually is. So the way to test it on a laptop is to give it an ESP-IDF that is not one.

What these headers are, and what they are not#

They are the narrowest possible stand-ins for the ESP-IDF and platform surface voice_loop.c names — nothing else in this repository includes them, and the real build never sees them. Each is on the include path only for the host iterate-kit-voice target.

They are not an emulator. The queue fake is a real bounded ring, because the loop's audio paths would be meaningless without one; everything else is a recorded no-op that returns the success the loop needs to get past boot. Nothing here schedules: xTaskCreatePinnedToCore records the task and returns pdPASS without running it, so a host test drives iterate_kit_voice_loop_step, _capture_step and _playback_step itself, one thread, in whatever order the test is about. That is the reason those three entry points exist.

fake_esp_idf.h exposes the handful of observations a test needs — how many tasks were created, whether the device asked to restart — plus iterate_kit_fake_esp_idf_reset(), which every fixture must call because all of this is file-static, exactly like the firmware it stands in for.

The platform half#

fake_esp_idf_platform.c stands in for the four platform modules the loop calls — provisioning, reset reason, restart note, and the itx transport — and it implements the real headers, so every struct has its real layout and every call its real signature.

The transport fake is also the way a test gets a message INTO the device. It receives options.connection in prepare() exactly as the real transport does, so iterate_kit_fake_platform_connection() hands a test the same session the socket would feed. A remote call is therefore the bytes a caller sends, not a hook: no accessor had to be added to loop.h for any of this.

It also owns the hop. iterate_kit_fake_platform_set_hop_answers(false) is a half-open socket — TCP accepting everything and nothing coming back — which is the failure the press probe exists for and cannot otherwise be reproduced.

The rule#

If a test needs a behaviour these fakes do not have, add the behaviour here rather than reaching around them. A test that bypasses the seam is testing the test.

Was this page helpful?