velxio/test/backend/unit/test_esp32_wifi_args.py

128 lines
5.1 KiB
Python
Raw Normal View History

"""
fix(esp32): the emulated chip keeps its radio, whatever the scan thinks (#260) `WiFi.begin()` panicked the guest: `Guru Meditation Error: Core 0 panic'ed (LoadStorePIFAddrError)`, EXCVADDR 0x60033c00. Reported against a self-hosted dev setup, and it did not reproduce on velxio.dev — which routes every supported ESP32 kind to the in-browser engines. The engines are not in this repo, so anyone self-hosting from it always runs QEMU, which is where the crash lives. It was verified away on a path the reporter cannot take. 0x60033c00 is the WiFi MAC at its APB alias, and the fork only instantiates that device when a NIC is attached (`qemu_find_nic_info(TYPE_ESP32_WIFI)` in hw/xtensa/esp32.c). The worker attached one only when `wifi_enabled` was true, so a regex over the user's source decided whether the machine had a radio at all. Reproduced against the real worker: same sketch, same firmware, only the flag differs — true connects on 192.168.4.x, false panics exactly as reported. Two things were wrong, and the second is why the first was fatal. The scan was too narrow. Arduino mode read only the entry sketch while pure-IDF mode read every file, so `#include <WiFi.h>` in a project header was judged WiFi-less. Verified against the live backend: main sketch -> has_wifi=true, the same include one file away -> false. Both branches read the whole project now. The frontend also trusted a false over its own all-files scan; the two signals are OR'd, because a stale or narrower false is the direction that hurts. But no scan should have been load-bearing. A real ESP32 has a radio whether or not the sketch uses it, so the emulated one does too: the NIC is attached unconditionally on machines that model a MAC. A misread sketch now fails to connect instead of faulting. Only those machines — the S3 machine models no radio (hw/xtensa/esp32s3.c never looks for the NIC), so handing it one would leave an unconsumed netdev. `wifi_enabled` still gates the host forward, which exposes the guest's server and does belong to the sketch. Measured before making it unconditional, on a sketch that never touches WiFi: boot time, guest-clock-vs-real-time and container CPU unchanged over 4 paired runs. The AP's beacon timer runs on QEMU_CLOCK_REALTIME — the reason -icount is off for Xtensa, b0fe5b5d — but idle beacons cost nothing measurable. One host, Linux x86-64, one sim at a time; small effects would not show. With the radio always present, the MicroPython `network` stub loses its reason to exist on this path: it was there because `WLAN(STA_IF)` hangs forever with no NIC behind it. Keeping the old condition would put the stub in front of a working device whenever the scan misread the sketch — #262, from the other direction. It now depends only on whether the backend models a radio. And when this class of fault does happen, the dump says "memory" and nothing else; a week went into #260 for that reason. LoadStorePIFAddrError is always an unmapped peripheral, so the worker now says so in the serial monitor, naming the WiFi MAC when the address is in its page. test_esp32_wifi_args re-implemented the arg logic in its own `_simulate_args`, so it would have passed unchanged through this entire bug — the same trap 1800f572 called out for the detector. The decision is now `wifi_nic_arg()` and the test calls it. Verified end to end against the QEMU worker: the multi-file WiFi project compiles with has_wifi=true and connects, and a WiFi-less sketch still boots and runs with the radio attached.
2026-08-22 02:37:50 +07:00
Tests for the WiFi NIC argument the ESP32 QEMU launch builds.
fix(esp32): the emulated chip keeps its radio, whatever the scan thinks (#260) `WiFi.begin()` panicked the guest: `Guru Meditation Error: Core 0 panic'ed (LoadStorePIFAddrError)`, EXCVADDR 0x60033c00. Reported against a self-hosted dev setup, and it did not reproduce on velxio.dev — which routes every supported ESP32 kind to the in-browser engines. The engines are not in this repo, so anyone self-hosting from it always runs QEMU, which is where the crash lives. It was verified away on a path the reporter cannot take. 0x60033c00 is the WiFi MAC at its APB alias, and the fork only instantiates that device when a NIC is attached (`qemu_find_nic_info(TYPE_ESP32_WIFI)` in hw/xtensa/esp32.c). The worker attached one only when `wifi_enabled` was true, so a regex over the user's source decided whether the machine had a radio at all. Reproduced against the real worker: same sketch, same firmware, only the flag differs — true connects on 192.168.4.x, false panics exactly as reported. Two things were wrong, and the second is why the first was fatal. The scan was too narrow. Arduino mode read only the entry sketch while pure-IDF mode read every file, so `#include <WiFi.h>` in a project header was judged WiFi-less. Verified against the live backend: main sketch -> has_wifi=true, the same include one file away -> false. Both branches read the whole project now. The frontend also trusted a false over its own all-files scan; the two signals are OR'd, because a stale or narrower false is the direction that hurts. But no scan should have been load-bearing. A real ESP32 has a radio whether or not the sketch uses it, so the emulated one does too: the NIC is attached unconditionally on machines that model a MAC. A misread sketch now fails to connect instead of faulting. Only those machines — the S3 machine models no radio (hw/xtensa/esp32s3.c never looks for the NIC), so handing it one would leave an unconsumed netdev. `wifi_enabled` still gates the host forward, which exposes the guest's server and does belong to the sketch. Measured before making it unconditional, on a sketch that never touches WiFi: boot time, guest-clock-vs-real-time and container CPU unchanged over 4 paired runs. The AP's beacon timer runs on QEMU_CLOCK_REALTIME — the reason -icount is off for Xtensa, b0fe5b5d — but idle beacons cost nothing measurable. One host, Linux x86-64, one sim at a time; small effects would not show. With the radio always present, the MicroPython `network` stub loses its reason to exist on this path: it was there because `WLAN(STA_IF)` hangs forever with no NIC behind it. Keeping the old condition would put the stub in front of a working device whenever the scan misread the sketch — #262, from the other direction. It now depends only on whether the backend models a radio. And when this class of fault does happen, the dump says "memory" and nothing else; a week went into #260 for that reason. LoadStorePIFAddrError is always an unmapped peripheral, so the worker now says so in the serial monitor, naming the WiFi MAC when the address is in its page. test_esp32_wifi_args re-implemented the arg logic in its own `_simulate_args`, so it would have passed unchanged through this entire bug — the same trap 1800f572 called out for the detector. The decision is now `wifi_nic_arg()` and the test calls it. Verified end to end against the QEMU worker: the multi-file WiFi project compiles with has_wifi=true and connects, and a WiFi-less sketch still boots and runs with the radio attached.
2026-08-22 02:37:50 +07:00
These call the SHIPPED `wifi_nic_arg`. The previous version of this file
re-implemented the logic inline, so it only ever proved that copy
self-consistent — it would have passed unchanged through issue #260, whose
whole content is that this decision was wrong.
"""
import unittest
from unittest.mock import patch, MagicMock
fix(esp32): the emulated chip keeps its radio, whatever the scan thinks (#260) `WiFi.begin()` panicked the guest: `Guru Meditation Error: Core 0 panic'ed (LoadStorePIFAddrError)`, EXCVADDR 0x60033c00. Reported against a self-hosted dev setup, and it did not reproduce on velxio.dev — which routes every supported ESP32 kind to the in-browser engines. The engines are not in this repo, so anyone self-hosting from it always runs QEMU, which is where the crash lives. It was verified away on a path the reporter cannot take. 0x60033c00 is the WiFi MAC at its APB alias, and the fork only instantiates that device when a NIC is attached (`qemu_find_nic_info(TYPE_ESP32_WIFI)` in hw/xtensa/esp32.c). The worker attached one only when `wifi_enabled` was true, so a regex over the user's source decided whether the machine had a radio at all. Reproduced against the real worker: same sketch, same firmware, only the flag differs — true connects on 192.168.4.x, false panics exactly as reported. Two things were wrong, and the second is why the first was fatal. The scan was too narrow. Arduino mode read only the entry sketch while pure-IDF mode read every file, so `#include <WiFi.h>` in a project header was judged WiFi-less. Verified against the live backend: main sketch -> has_wifi=true, the same include one file away -> false. Both branches read the whole project now. The frontend also trusted a false over its own all-files scan; the two signals are OR'd, because a stale or narrower false is the direction that hurts. But no scan should have been load-bearing. A real ESP32 has a radio whether or not the sketch uses it, so the emulated one does too: the NIC is attached unconditionally on machines that model a MAC. A misread sketch now fails to connect instead of faulting. Only those machines — the S3 machine models no radio (hw/xtensa/esp32s3.c never looks for the NIC), so handing it one would leave an unconsumed netdev. `wifi_enabled` still gates the host forward, which exposes the guest's server and does belong to the sketch. Measured before making it unconditional, on a sketch that never touches WiFi: boot time, guest-clock-vs-real-time and container CPU unchanged over 4 paired runs. The AP's beacon timer runs on QEMU_CLOCK_REALTIME — the reason -icount is off for Xtensa, b0fe5b5d — but idle beacons cost nothing measurable. One host, Linux x86-64, one sim at a time; small effects would not show. With the radio always present, the MicroPython `network` stub loses its reason to exist on this path: it was there because `WLAN(STA_IF)` hangs forever with no NIC behind it. Keeping the old condition would put the stub in front of a working device whenever the scan misread the sketch — #262, from the other direction. It now depends only on whether the backend models a radio. And when this class of fault does happen, the dump says "memory" and nothing else; a week went into #260 for that reason. LoadStorePIFAddrError is always an unmapped peripheral, so the worker now says so in the serial monitor, naming the WiFi MAC when the address is in its page. test_esp32_wifi_args re-implemented the arg logic in its own `_simulate_args`, so it would have passed unchanged through this entire bug — the same trap 1800f572 called out for the detector. The decision is now `wifi_nic_arg()` and the test calls it. Verified end to end against the QEMU worker: the multi-file WiFi project compiles with has_wifi=true and connects, and a WiFi-less sketch still boots and runs with the radio attached.
2026-08-22 02:37:50 +07:00
from app.services.esp32_worker import wifi_nic_arg
fix(esp32): the emulated chip keeps its radio, whatever the scan thinks (#260) `WiFi.begin()` panicked the guest: `Guru Meditation Error: Core 0 panic'ed (LoadStorePIFAddrError)`, EXCVADDR 0x60033c00. Reported against a self-hosted dev setup, and it did not reproduce on velxio.dev — which routes every supported ESP32 kind to the in-browser engines. The engines are not in this repo, so anyone self-hosting from it always runs QEMU, which is where the crash lives. It was verified away on a path the reporter cannot take. 0x60033c00 is the WiFi MAC at its APB alias, and the fork only instantiates that device when a NIC is attached (`qemu_find_nic_info(TYPE_ESP32_WIFI)` in hw/xtensa/esp32.c). The worker attached one only when `wifi_enabled` was true, so a regex over the user's source decided whether the machine had a radio at all. Reproduced against the real worker: same sketch, same firmware, only the flag differs — true connects on 192.168.4.x, false panics exactly as reported. Two things were wrong, and the second is why the first was fatal. The scan was too narrow. Arduino mode read only the entry sketch while pure-IDF mode read every file, so `#include <WiFi.h>` in a project header was judged WiFi-less. Verified against the live backend: main sketch -> has_wifi=true, the same include one file away -> false. Both branches read the whole project now. The frontend also trusted a false over its own all-files scan; the two signals are OR'd, because a stale or narrower false is the direction that hurts. But no scan should have been load-bearing. A real ESP32 has a radio whether or not the sketch uses it, so the emulated one does too: the NIC is attached unconditionally on machines that model a MAC. A misread sketch now fails to connect instead of faulting. Only those machines — the S3 machine models no radio (hw/xtensa/esp32s3.c never looks for the NIC), so handing it one would leave an unconsumed netdev. `wifi_enabled` still gates the host forward, which exposes the guest's server and does belong to the sketch. Measured before making it unconditional, on a sketch that never touches WiFi: boot time, guest-clock-vs-real-time and container CPU unchanged over 4 paired runs. The AP's beacon timer runs on QEMU_CLOCK_REALTIME — the reason -icount is off for Xtensa, b0fe5b5d — but idle beacons cost nothing measurable. One host, Linux x86-64, one sim at a time; small effects would not show. With the radio always present, the MicroPython `network` stub loses its reason to exist on this path: it was there because `WLAN(STA_IF)` hangs forever with no NIC behind it. Keeping the old condition would put the stub in front of a working device whenever the scan misread the sketch — #262, from the other direction. It now depends only on whether the backend models a radio. And when this class of fault does happen, the dump says "memory" and nothing else; a week went into #260 for that reason. LoadStorePIFAddrError is always an unmapped peripheral, so the worker now says so in the serial monitor, naming the WiFi MAC when the address is in its page. test_esp32_wifi_args re-implemented the arg logic in its own `_simulate_args`, so it would have passed unchanged through this entire bug — the same trap 1800f572 called out for the detector. The decision is now `wifi_nic_arg()` and the test calls it. Verified end to end against the QEMU worker: the multi-file WiFi project compiles with has_wifi=true and connects, and a WiFi-less sketch still boots and runs with the radio attached.
2026-08-22 02:37:50 +07:00
class TestWifiNicArg(unittest.TestCase):
"""What the machine gets, and why."""
def test_radio_attached_even_when_the_sketch_looks_wifi_less(self):
"""The regression #260 is about.
A real ESP32 has a radio whether or not the sketch uses it. When the
NIC was conditional, a sketch misread as WiFi-less ran on a machine
with nothing mapped at the MAC, and the firmware's first register
touch panicked with LoadStorePIFAddrError instead of failing to
connect.
"""
arg = wifi_nic_arg('esp32-picsimlab', wifi_enabled=False)
self.assertIsNotNone(arg)
self.assertIn('model=esp32_wifi', arg)
self.assertIn('net=192.168.4.0/24', arg)
def test_classic_esp32_model(self):
arg = wifi_nic_arg('esp32-picsimlab', wifi_enabled=True)
self.assertIn('model=esp32_wifi', arg)
def test_c3_uses_its_own_model(self):
arg = wifi_nic_arg('esp32c3-picsimlab', wifi_enabled=True)
self.assertIn('model=esp32c3_wifi', arg)
def test_s3_gets_no_nic_because_it_models_no_radio(self):
"""hw/xtensa/esp32s3.c never looks for the NIC, so handing it one
would leave an unconsumed netdev."""
self.assertIsNone(wifi_nic_arg('esp32s3-picsimlab', wifi_enabled=True))
self.assertIsNone(wifi_nic_arg('esp32s3-picsimlab', wifi_enabled=False))
def test_hostfwd_included_when_port_set(self):
fix(esp32): the emulated chip keeps its radio, whatever the scan thinks (#260) `WiFi.begin()` panicked the guest: `Guru Meditation Error: Core 0 panic'ed (LoadStorePIFAddrError)`, EXCVADDR 0x60033c00. Reported against a self-hosted dev setup, and it did not reproduce on velxio.dev — which routes every supported ESP32 kind to the in-browser engines. The engines are not in this repo, so anyone self-hosting from it always runs QEMU, which is where the crash lives. It was verified away on a path the reporter cannot take. 0x60033c00 is the WiFi MAC at its APB alias, and the fork only instantiates that device when a NIC is attached (`qemu_find_nic_info(TYPE_ESP32_WIFI)` in hw/xtensa/esp32.c). The worker attached one only when `wifi_enabled` was true, so a regex over the user's source decided whether the machine had a radio at all. Reproduced against the real worker: same sketch, same firmware, only the flag differs — true connects on 192.168.4.x, false panics exactly as reported. Two things were wrong, and the second is why the first was fatal. The scan was too narrow. Arduino mode read only the entry sketch while pure-IDF mode read every file, so `#include <WiFi.h>` in a project header was judged WiFi-less. Verified against the live backend: main sketch -> has_wifi=true, the same include one file away -> false. Both branches read the whole project now. The frontend also trusted a false over its own all-files scan; the two signals are OR'd, because a stale or narrower false is the direction that hurts. But no scan should have been load-bearing. A real ESP32 has a radio whether or not the sketch uses it, so the emulated one does too: the NIC is attached unconditionally on machines that model a MAC. A misread sketch now fails to connect instead of faulting. Only those machines — the S3 machine models no radio (hw/xtensa/esp32s3.c never looks for the NIC), so handing it one would leave an unconsumed netdev. `wifi_enabled` still gates the host forward, which exposes the guest's server and does belong to the sketch. Measured before making it unconditional, on a sketch that never touches WiFi: boot time, guest-clock-vs-real-time and container CPU unchanged over 4 paired runs. The AP's beacon timer runs on QEMU_CLOCK_REALTIME — the reason -icount is off for Xtensa, b0fe5b5d — but idle beacons cost nothing measurable. One host, Linux x86-64, one sim at a time; small effects would not show. With the radio always present, the MicroPython `network` stub loses its reason to exist on this path: it was there because `WLAN(STA_IF)` hangs forever with no NIC behind it. Keeping the old condition would put the stub in front of a working device whenever the scan misread the sketch — #262, from the other direction. It now depends only on whether the backend models a radio. And when this class of fault does happen, the dump says "memory" and nothing else; a week went into #260 for that reason. LoadStorePIFAddrError is always an unmapped peripheral, so the worker now says so in the serial monitor, naming the WiFi MAC when the address is in its page. test_esp32_wifi_args re-implemented the arg logic in its own `_simulate_args`, so it would have passed unchanged through this entire bug — the same trap 1800f572 called out for the detector. The decision is now `wifi_nic_arg()` and the test calls it. Verified end to end against the QEMU worker: the multi-file WiFi project compiles with has_wifi=true and connects, and a WiFi-less sketch still boots and runs with the radio attached.
2026-08-22 02:37:50 +07:00
arg = wifi_nic_arg('esp32-picsimlab', wifi_enabled=True, hostfwd_port=12345)
self.assertIn('hostfwd=tcp::12345-192.168.4.15:80', arg)
def test_hostfwd_absent_when_port_zero(self):
fix(esp32): the emulated chip keeps its radio, whatever the scan thinks (#260) `WiFi.begin()` panicked the guest: `Guru Meditation Error: Core 0 panic'ed (LoadStorePIFAddrError)`, EXCVADDR 0x60033c00. Reported against a self-hosted dev setup, and it did not reproduce on velxio.dev — which routes every supported ESP32 kind to the in-browser engines. The engines are not in this repo, so anyone self-hosting from it always runs QEMU, which is where the crash lives. It was verified away on a path the reporter cannot take. 0x60033c00 is the WiFi MAC at its APB alias, and the fork only instantiates that device when a NIC is attached (`qemu_find_nic_info(TYPE_ESP32_WIFI)` in hw/xtensa/esp32.c). The worker attached one only when `wifi_enabled` was true, so a regex over the user's source decided whether the machine had a radio at all. Reproduced against the real worker: same sketch, same firmware, only the flag differs — true connects on 192.168.4.x, false panics exactly as reported. Two things were wrong, and the second is why the first was fatal. The scan was too narrow. Arduino mode read only the entry sketch while pure-IDF mode read every file, so `#include <WiFi.h>` in a project header was judged WiFi-less. Verified against the live backend: main sketch -> has_wifi=true, the same include one file away -> false. Both branches read the whole project now. The frontend also trusted a false over its own all-files scan; the two signals are OR'd, because a stale or narrower false is the direction that hurts. But no scan should have been load-bearing. A real ESP32 has a radio whether or not the sketch uses it, so the emulated one does too: the NIC is attached unconditionally on machines that model a MAC. A misread sketch now fails to connect instead of faulting. Only those machines — the S3 machine models no radio (hw/xtensa/esp32s3.c never looks for the NIC), so handing it one would leave an unconsumed netdev. `wifi_enabled` still gates the host forward, which exposes the guest's server and does belong to the sketch. Measured before making it unconditional, on a sketch that never touches WiFi: boot time, guest-clock-vs-real-time and container CPU unchanged over 4 paired runs. The AP's beacon timer runs on QEMU_CLOCK_REALTIME — the reason -icount is off for Xtensa, b0fe5b5d — but idle beacons cost nothing measurable. One host, Linux x86-64, one sim at a time; small effects would not show. With the radio always present, the MicroPython `network` stub loses its reason to exist on this path: it was there because `WLAN(STA_IF)` hangs forever with no NIC behind it. Keeping the old condition would put the stub in front of a working device whenever the scan misread the sketch — #262, from the other direction. It now depends only on whether the backend models a radio. And when this class of fault does happen, the dump says "memory" and nothing else; a week went into #260 for that reason. LoadStorePIFAddrError is always an unmapped peripheral, so the worker now says so in the serial monitor, naming the WiFi MAC when the address is in its page. test_esp32_wifi_args re-implemented the arg logic in its own `_simulate_args`, so it would have passed unchanged through this entire bug — the same trap 1800f572 called out for the detector. The decision is now `wifi_nic_arg()` and the test calls it. Verified end to end against the QEMU worker: the multi-file WiFi project compiles with has_wifi=true and connects, and a WiFi-less sketch still boots and runs with the radio attached.
2026-08-22 02:37:50 +07:00
arg = wifi_nic_arg('esp32-picsimlab', wifi_enabled=True, hostfwd_port=0)
self.assertNotIn('hostfwd', arg)
def test_hostfwd_needs_a_sketch_that_serves(self):
"""The forward exposes the guest's server to the host. The radio is
unconditional; opening a port is not."""
arg = wifi_nic_arg('esp32-picsimlab', wifi_enabled=False, hostfwd_port=12345)
self.assertNotIn('hostfwd', arg)
class TestEspQemuManagerWifiArgs(unittest.TestCase):
"""Test that EspQemuManager passes wifi params through."""
def test_start_instance_accepts_wifi_params(self):
"""start_instance should accept wifi_enabled and wifi_hostfwd_port."""
from app.services.esp_qemu_manager import EspQemuManager
mgr = EspQemuManager()
# start_instance calls `asyncio.create_task(self._boot(...))`. The
# `_boot(...)` call creates a coroutine BEFORE create_task sees it,
# so simply mocking create_task with no side-effect lets the
# coroutine leak and trigger a "coroutine never awaited"
# RuntimeWarning in the test log. Close the coroutine inside the
# mock to consume it cleanly.
def consume_coroutine(coro):
coro.close()
return MagicMock()
with patch('asyncio.create_task', side_effect=consume_coroutine):
mgr.start_instance(
'test-client', 'esp32', MagicMock(),
firmware_b64=None,
wifi_enabled=True,
wifi_hostfwd_port=8080,
)
class TestSimulationWifiPort(unittest.TestCase):
"""Test that simulation.py allocates a free port for WiFi hostfwd."""
def test_find_free_port(self):
from app.api.routes.simulation import _find_free_port
port = _find_free_port()
self.assertIsInstance(port, int)
self.assertGreater(port, 0)
self.assertLess(port, 65536)
if __name__ == '__main__':
unittest.main()
fix(esp32): the emulated chip keeps its radio, whatever the scan thinks (#260) `WiFi.begin()` panicked the guest: `Guru Meditation Error: Core 0 panic'ed (LoadStorePIFAddrError)`, EXCVADDR 0x60033c00. Reported against a self-hosted dev setup, and it did not reproduce on velxio.dev — which routes every supported ESP32 kind to the in-browser engines. The engines are not in this repo, so anyone self-hosting from it always runs QEMU, which is where the crash lives. It was verified away on a path the reporter cannot take. 0x60033c00 is the WiFi MAC at its APB alias, and the fork only instantiates that device when a NIC is attached (`qemu_find_nic_info(TYPE_ESP32_WIFI)` in hw/xtensa/esp32.c). The worker attached one only when `wifi_enabled` was true, so a regex over the user's source decided whether the machine had a radio at all. Reproduced against the real worker: same sketch, same firmware, only the flag differs — true connects on 192.168.4.x, false panics exactly as reported. Two things were wrong, and the second is why the first was fatal. The scan was too narrow. Arduino mode read only the entry sketch while pure-IDF mode read every file, so `#include <WiFi.h>` in a project header was judged WiFi-less. Verified against the live backend: main sketch -> has_wifi=true, the same include one file away -> false. Both branches read the whole project now. The frontend also trusted a false over its own all-files scan; the two signals are OR'd, because a stale or narrower false is the direction that hurts. But no scan should have been load-bearing. A real ESP32 has a radio whether or not the sketch uses it, so the emulated one does too: the NIC is attached unconditionally on machines that model a MAC. A misread sketch now fails to connect instead of faulting. Only those machines — the S3 machine models no radio (hw/xtensa/esp32s3.c never looks for the NIC), so handing it one would leave an unconsumed netdev. `wifi_enabled` still gates the host forward, which exposes the guest's server and does belong to the sketch. Measured before making it unconditional, on a sketch that never touches WiFi: boot time, guest-clock-vs-real-time and container CPU unchanged over 4 paired runs. The AP's beacon timer runs on QEMU_CLOCK_REALTIME — the reason -icount is off for Xtensa, b0fe5b5d — but idle beacons cost nothing measurable. One host, Linux x86-64, one sim at a time; small effects would not show. With the radio always present, the MicroPython `network` stub loses its reason to exist on this path: it was there because `WLAN(STA_IF)` hangs forever with no NIC behind it. Keeping the old condition would put the stub in front of a working device whenever the scan misread the sketch — #262, from the other direction. It now depends only on whether the backend models a radio. And when this class of fault does happen, the dump says "memory" and nothing else; a week went into #260 for that reason. LoadStorePIFAddrError is always an unmapped peripheral, so the worker now says so in the serial monitor, naming the WiFi MAC when the address is in its page. test_esp32_wifi_args re-implemented the arg logic in its own `_simulate_args`, so it would have passed unchanged through this entire bug — the same trap 1800f572 called out for the detector. The decision is now `wifi_nic_arg()` and the test calls it. Verified end to end against the QEMU worker: the multi-file WiFi project compiles with has_wifi=true and connects, and a WiFi-less sketch still boots and runs with the radio attached.
2026-08-22 02:37:50 +07:00
class TestExplainPifFault(unittest.TestCase):
"""The panic dump says "memory"; the user needs to hear "peripheral"."""
def test_names_the_wifi_mac_at_the_apb_alias(self):
from app.services.esp32_worker import _explain_pif_fault
note = _explain_pif_fault(b'EXCVADDR: 0x60033c00\n')
self.assertIsNotNone(note)
self.assertIn('WiFi MAC', note)
self.assertIn('0x60033c00', note)
def test_names_the_wifi_mac_at_the_dport_address(self):
from app.services.esp32_worker import _explain_pif_fault
note = _explain_pif_fault(b'EXCVADDR: 0x3ff73c00\n')
self.assertIn('WiFi MAC', note)
def test_other_addresses_are_still_explained_generically(self):
from app.services.esp32_worker import _explain_pif_fault
note = _explain_pif_fault(b'EXCVADDR: 0x3ff40000\n')
self.assertIn('a peripheral', note)
self.assertNotIn('WiFi MAC', note)
def test_waits_for_the_address(self):
"""The cause and EXCVADDR arrive in different chunks."""
from app.services.esp32_worker import _explain_pif_fault
self.assertIsNone(_explain_pif_fault(b'Core 0 register dump:\n'))