diff --git a/meshtastic/tests/conftest.py b/meshtastic/tests/conftest.py index d31fa31..596f4b1 100644 --- a/meshtastic/tests/conftest.py +++ b/meshtastic/tests/conftest.py @@ -15,6 +15,7 @@ from .firmware_harness import ( find_meshtasticd, is_compatible_host, ) +from .fw_helpers import set_region # Use a different base port for the single-node fixture so it doesn't # conflict with the multi-node mesh fixture. @@ -41,11 +42,14 @@ def firmware_node(): tests be order-independent. Yields the SimNode instance. The node is booted with a fresh erased - config and listens on localhost at its TCP port. + config and listens on localhost at its TCP port. Region is set to US + so modem-preset tests work against firmware >= 2.8, which clamps + presets to the legal set for the current region. """ _skip_firmware_if_unavailable() mesh = SimMesh(n_nodes=1, base_port=SINGLE_NODE_BASE_PORT) mesh.start() + set_region(mesh.get_node(0).port, "US") yield mesh.get_node(0) mesh.stop() @@ -55,11 +59,24 @@ def firmware_mesh(): """A 3-node chain (A-B-C) meshtasticd sim mesh for smokemesh tests. Yields the SimMesh instance. Nodes are connected and the SIMULATOR_APP - packet bridge is running. Node DB convergence is awaited. + packet bridge is running. Region is set to US for firmware >= 2.8 + compatibility, interfaces are reconnected after the region change, and + node DB convergence is awaited. """ _skip_firmware_if_unavailable() mesh = SimMesh(n_nodes=3, topology=CHAIN_TOPOLOGY) mesh.start() + for node in mesh.nodes: + set_region(node.port, "US") + # The region commit restarts each node's TCP listener, so reconnect the + # harness interfaces before waiting for convergence. + for node in mesh.nodes: + if node.iface is not None: + try: + node.iface.close() + except Exception: # pylint: disable=broad-except + pass + node.connect() mesh.wait_for_convergence(timeout=30) yield mesh mesh.stop() diff --git a/meshtastic/tests/fw_helpers.py b/meshtastic/tests/fw_helpers.py index f570f09..2bc2988 100644 --- a/meshtastic/tests/fw_helpers.py +++ b/meshtastic/tests/fw_helpers.py @@ -31,6 +31,11 @@ logger = logging.getLogger(__name__) # simulator happy when many short-lived connections are happening. PAUSE_AFTER_CLI = 0.2 +# Pause after changing the LoRa region. The firmware may restart the TCP +# listener while committing the new radio config, so give it time to settle +# before the next CLI call or harness reconnect. +PAUSE_AFTER_REGION_CHANGE = 2.0 + # --------------------------------------------------------------------------- # CLI invocation @@ -325,6 +330,19 @@ def subscribe_positions(iface: TCPInterface) -> PacketCollector: return _subscribe_topic(iface, "meshtastic.receive.position") +def set_region(port: int, region: str = "US") -> None: + """Set the LoRa region on a sim node via the CLI. + + The node briefly restarts its TCP listener while committing the radio + config; ``run_cli()`` handles the reconnect retry, and we sleep long + enough for the new region to take effect before callers continue. + """ + rc, out = run_cli(port, "--set", "lora.region", region) + if rc != 0: + raise RuntimeError(f"Failed to set lora.region={region}: {out}") + time.sleep(PAUSE_AFTER_REGION_CHANGE) + + def unsubscribe_all(topic: str) -> None: """Drop every handler currently registered on *topic*. @@ -339,12 +357,14 @@ def unsubscribe_all(topic: str) -> None: __all__ = [ "PAUSE_AFTER_CLI", + "PAUSE_AFTER_REGION_CHANGE", "PacketCollector", "RECEIVE_TIMEOUT", "cli_then_verify", "connect_iface", "resolve_cli", "run_cli", + "set_region", "subscribe_positions", "subscribe_telemetries", "subscribe_texts",