75 lines
3.6 KiB
Python
75 lines
3.6 KiB
Python
"""Primary/Ethernet coexistence harness guards; never opens hardware."""
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
from unittest.mock import Mock, patch
|
|
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools/hil"))
|
|
import ethernet_primary_probe as probe
|
|
|
|
|
|
class FakeNode:
|
|
def __init__(self, radio="> 909.5,500.000,5,5", ethernet="> 909.500,500,5,5,32; sync=0x16; saved"):
|
|
self.identity = {"port": "fake-only"}
|
|
self.commands = []
|
|
self.radio = radio
|
|
self.ethernet = ethernet
|
|
|
|
def command(self, text):
|
|
self.commands.append(text)
|
|
return {"get radio": self.radio, "get ethernet.radio": self.ethernet,
|
|
"get ethernet": "> ethernet=on; active (RX time-shared); TCP 5003 connected",
|
|
"get tx.channel 0": '> tx.channel 0 "Public" radio; active=radio',
|
|
"set tx.channel 0 radio": 'OK - tx.channel 0 "Public" radio; active=radio',
|
|
"show channels on": "Terminal display: channels on"}.get(text, "> public")
|
|
|
|
|
|
class EthernetPrimaryProbeTests(unittest.TestCase):
|
|
def test_lab_tuple_parser_is_strict_and_accepts_known_cli_formats(self):
|
|
for text in ("> 909.5,500,5,5", "get radio\r\n> 909.500,500.000,5,5\r\n> ",
|
|
"get radio\r\n -> > 909.5,500,5,5\r\n> ",
|
|
"> 909.500,500,5,5,32; sync=0x16; saved"):
|
|
probe.assert_lab_radio(text)
|
|
for text in ("> 910.525,500,5,5", "> 909.5,125,5,5", "> 909.5,500,7,5",
|
|
"> 909.5,500,5,7", "Error: unavailable", "set radio 909.5,500,5,5"):
|
|
with self.assertRaises(RuntimeError):
|
|
probe.assert_lab_radio(text)
|
|
|
|
def test_own_command_echo_is_never_an_incoming_message(self):
|
|
marker = "ETH-PRIMARY-12345678-a"
|
|
for text in ("channel 0 " + marker, "Sent to Public.\r\n> ",
|
|
"debug incoming " + marker, "CHANNEL MSG -> Public (FLOOD)\r\n wrong-message"):
|
|
self.assertFalse(probe.received_channel_marker(text, marker))
|
|
self.assertTrue(probe.received_channel_marker(
|
|
"\r\nCHANNEL MSG -> Public (FLOOD)\r\n EthernetA: " + marker + "\r\n> ", marker))
|
|
self.assertFalse(probe.received_channel_marker(
|
|
"CHANNEL MSG -> Public (FLOOD)\r\n Node: " + marker + "-different", marker))
|
|
|
|
def test_bad_primary_or_ethernet_profile_fails_before_any_tx(self):
|
|
for name in ("radio", "ethernet"):
|
|
a, b = FakeNode(), FakeNode()
|
|
setattr(b, name, "> 910.525,500,5,5")
|
|
factory = Mock()
|
|
with self.assertRaises(RuntimeError):
|
|
probe.run_primary(a, b, "tcp://a:5003", "tcp://b:5003", factory=factory)
|
|
factory.assert_not_called()
|
|
self.assertFalse(any(command.startswith("set ") or command.startswith("channel ")
|
|
for node in (a, b) for command in node.commands))
|
|
|
|
def test_channel_zero_primary_policy_is_set_and_confirmed_before_kiss(self):
|
|
a, b = FakeNode(), FakeNode()
|
|
factory = Mock(side_effect=RuntimeError("stop before opening test socket"))
|
|
with self.assertRaisesRegex(RuntimeError, "stop before"):
|
|
probe.run_primary(a, b, "tcp://a:5003", "tcp://b:5003", factory=factory)
|
|
for node in (a, b):
|
|
self.assertIn("set tx.channel 0 radio", node.commands)
|
|
self.assertIn("show channels on", node.commands)
|
|
self.assertLess(node.commands.index("set tx.channel 0 radio"),
|
|
len(node.commands) - 1 - node.commands[::-1].index("get tx.channel 0"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|