velxio/test/backend/unit/test_esp32_wifi_args.py

128 lines
5.1 KiB
Python

"""
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'))