""" Tests for the WiFi NIC argument the ESP32 QEMU launch builds. 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 from app.services.esp32_worker import wifi_nic_arg 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): 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): 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() 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'))