Files
MeshCore/test/test_rs232_driver_lifecycle.py
mikecarper 0be4c7488c Add bounded Morse receive and negotiated RS232 autosleep
Add native narrow-band CW capture, ACK-first remote sessions, retained LoRa-readable pages, checked restoration and transport-safe progress. Add stock-compatible negotiated heartbeat/autosleep and recovery. Include host regressions and identity-bound HIL runners; keep unverified faster tuning HIL-only and document outstanding hardware validation.
2026-10-13 01:24:39 -07:00

138 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Execute the production ESP32 UART begin/end and bridge state checks."""
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
from test_replay_reset_integration import extract_braced
ROOT = Path(__file__).resolve().parents[1]
FIXTURE = r'''
#include <cassert>
#include <cstdint>
#define ESP32 1
#define BRIDGE_DEBUG_PRINTLN(...) ((void)0)
struct NodePrefs { uint32_t bridge_baud = 57600; };
struct Stream { virtual ~Stream() = default; };
struct HardwareSerial : Stream {
bool pins_ok = true, begin_ok = true, installed = false;
unsigned pin_calls = 0, begin_calls = 0, end_calls = 0;
int16_t rx = -1, tx = -1;
uint32_t baud = 0;
bool setPins(int16_t selected_rx, int16_t selected_tx) {
++pin_calls; rx = selected_rx; tx = selected_tx; return pins_ok;
}
void begin(uint32_t selected_baud) { ++begin_calls; baud = selected_baud; installed = begin_ok; }
explicit operator bool() const { return installed; }
void end() { ++end_calls; installed = false; }
};
struct BridgeBase {
bool _initialized = false;
@RUNNING@
};
struct RS232Bridge : BridgeBase {
NodePrefs* _prefs;
Stream* _serial;
int16_t _rx_pin, _tx_pin;
uint16_t _rx_buffer_pos = 37;
bool _rx_packet_ready = true;
uint16_t _tx_size = 19, _tx_pos = 3;
bool _tx_wire_pending = true, _tx_is_ping = true;
uint32_t _tx_ping_challenge = 4, _outstanding_challenge = 5;
uint64_t _pong_target = 6;
uint32_t _pong_challenge = 7;
bool _ping_due = false, _partial_suspend_grace = true;
RS232Bridge(NodePrefs& prefs, HardwareSerial& serial)
: _prefs(&prefs), _serial(&serial), _rx_pin(5), _tx_pin(6) {}
void begin();
void end();
void resetTransportState();
};
@RESET@
@BEGIN@
@END@
int main() {
NodePrefs prefs;
HardwareSerial serial;
RS232Bridge bridge(prefs, serial);
auto dirty_parser = [&]() {
bridge._rx_buffer_pos = 37; bridge._rx_packet_ready = true;
bridge._tx_size = 19; bridge._tx_pos = 3;
bridge._tx_wire_pending = bridge._tx_is_ping = true;
bridge._tx_ping_challenge = 4; bridge._outstanding_challenge = 5;
bridge._pong_target = 6; bridge._pong_challenge = 7;
bridge._ping_due = false; bridge._partial_suspend_grace = true;
};
auto assert_parser_reset = [&]() {
assert(bridge._rx_buffer_pos == 0 && !bridge._rx_packet_ready);
assert(bridge._tx_size == 0 && bridge._tx_pos == 0);
assert(!bridge._tx_wire_pending && !bridge._tx_is_ping);
assert(bridge._tx_ping_challenge == 0 && bridge._outstanding_challenge == 0);
assert(bridge._pong_target == 0 && bridge._pong_challenge == 0);
assert(bridge._ping_due && !bridge._partial_suspend_grace);
};
assert(!bridge.isRunning());
serial.pins_ok = false;
bridge.begin();
assert(!bridge.isRunning() && serial.pin_calls == 1 && serial.begin_calls == 0);
assert_parser_reset();
serial.pins_ok = true; serial.begin_ok = false;
dirty_parser();
bridge.begin();
assert(!bridge.isRunning() && !serial.installed && serial.begin_calls == 1);
assert_parser_reset();
serial.begin_ok = true;
dirty_parser();
bridge.begin();
assert(bridge.isRunning() && serial.installed && serial.begin_calls == 2);
assert_parser_reset();
assert(serial.rx == 5 && serial.tx == 6 && serial.baud == 57600);
dirty_parser();
bridge.end();
assert(!bridge.isRunning() && !serial.installed && serial.end_calls == 1);
assert_parser_reset();
// A failed restart must clear its former state, and a later retry can recover.
dirty_parser(); bridge.begin(); assert(bridge.isRunning()); assert_parser_reset();
serial.pins_ok = false;
dirty_parser(); bridge.begin(); assert(!bridge.isRunning()); assert_parser_reset();
dirty_parser(); bridge.end(); assert_parser_reset();
serial.pins_ok = true; prefs.bridge_baud = 19200;
dirty_parser(); bridge.begin(); assert(bridge.isRunning() && serial.baud == 19200); assert_parser_reset();
for (unsigned cycle = 0; cycle < 100; ++cycle) {
dirty_parser(); bridge.end(); assert(!bridge.isRunning()); assert_parser_reset();
dirty_parser(); bridge.begin(); assert(bridge.isRunning()); assert_parser_reset();
}
dirty_parser(); bridge.end(); assert(!bridge.isRunning()); assert_parser_reset();
}
'''
class RS232DriverLifecycleTests(unittest.TestCase):
def test_failed_pins_or_driver_start_never_report_running_and_retry_recovers(self):
compiler = shutil.which("g++") or shutil.which("clang++")
self.assertIsNotNone(compiler, "a host C++ compiler is required")
driver = (ROOT / "src/helpers/bridges/RS232Bridge.cpp").read_text(encoding="ascii")
base = (ROOT / "src/helpers/bridges/BridgeBase.cpp").read_text(encoding="ascii")
code = FIXTURE.replace("@RUNNING@", extract_braced(base, "bool BridgeBase::isRunning() const")
.replace("BridgeBase::", "").replace(" override", ""))
code = code.replace("@BEGIN@", extract_braced(driver, "void RS232Bridge::begin()"))
code = code.replace("@END@", extract_braced(driver, "void RS232Bridge::end()"))
code = code.replace("@RESET@", extract_braced(driver, "void RS232Bridge::resetTransportState()"))
with tempfile.TemporaryDirectory(prefix="rs232-driver-lifecycle-") as directory:
work = Path(directory)
source, binary = work / "main.cpp", work / "test"
source.write_text(code, encoding="ascii")
built = subprocess.run([compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror",
"-fsanitize=address,undefined", "-fno-sanitize-recover=all", "-fno-pie", "-no-pie",
str(source), "-o", str(binary)], capture_output=True, text=True, timeout=60)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
checked = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10)
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
if __name__ == "__main__":
unittest.main()