Files
MeshCore/test/test_rs232_bridge_diagnostics.py
T

81 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""Sanitized production UART diagnostics, probes and legacy parser tests."""
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
FIXTURES = ROOT / "test/fixtures/rs232_heartbeat"
class RS232BridgeDiagnosticTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
compiler = shutil.which("g++") or shutil.which("clang++")
if not compiler:
raise AssertionError("a host C++ compiler is required")
cls.temporary = tempfile.TemporaryDirectory(prefix="rs232-diagnostics-")
cls.binaries = []
for platform, cooperative in (("ESP32", 0), ("ESP32", 1),
("NRF52_PLATFORM", 0),
("RP2040_PLATFORM", 0),
("STM32_PLATFORM", 0)):
binary = Path(cls.temporary.name) / f"diagnostics-{platform}-{cooperative}"
built = subprocess.run([
compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror",
"-fsanitize=address,undefined", "-fno-sanitize-recover=all",
"-fno-pie", "-no-pie", "-DARDUINO=1", f"-D{platform}=1",
"-DWITH_RS232_BRIDGE=1",
f"-DMESH_ESP32_USB_CONSOLE_COOPERATIVE={cooperative}",
"-I" + str(FIXTURES), "-I" + str(ROOT / "src"),
str(ROOT / "test/fixtures/rs232_bridge_diagnostics.cpp"),
str(ROOT / "src/helpers/bridges/RS232Bridge.cpp"),
"-o", str(binary),
], capture_output=True, text=True, timeout=60)
if built.returncode:
cls.temporary.cleanup()
raise AssertionError(built.stdout + built.stderr)
cls.binaries.append(binary)
@classmethod
def tearDownClass(cls):
cls.temporary.cleanup()
def run_case(self, name):
for binary in self.binaries:
with self.subTest(console=binary.name):
checked = subprocess.run([str(binary), name], capture_output=True,
text=True, timeout=60)
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
self.assertIn(name + " passed", checked.stdout)
def test_probe_codec_strict_integrity_version_and_bounds(self):
self.run_case("codec")
def test_probe_never_allocates_echoes_forwards_or_refreshes_peer_power(self):
self.run_case("isolation")
def test_both_real_legacy_parsers_ignore_reserved_frames(self):
self.run_case("legacy")
def test_all_partial_tx_prefixes_are_bounded_and_never_retransmitted(self):
self.run_case("backpressure")
def test_stopped_busy_and_heartbeat_owned_transports_reject_probe(self):
self.run_case("state")
def test_data_errors_pool_wait_and_lifetime_reset_are_observed(self):
self.run_case("counters")
def test_saturating_counters_and_all_cli_page_bounds(self):
self.run_case("bounds")
def test_usb_debug_both_gates_change_rate_limit_and_console_backpressure(self):
self.run_case("debug")
if __name__ == "__main__":
unittest.main()