#!/usr/bin/env python3 """Run actual delayed reply producers and bounded UART/TCP admission.""" from pathlib import Path import os import re import shutil import subprocess import sys import tempfile import unittest from test_replay_reset_integration import extract_braced ROOT = Path(__file__).resolve().parents[1] FIXTURE = ROOT / "test/fixtures/companion_delayed_reply_delivery/test.cpp" SANITIZERS = (["-fsanitize=address,undefined", "-fno-sanitize-recover=all", "-fno-pie", "-no-pie"] if sys.platform.startswith("linux") else []) def production_methods(source, base, packet, dispatcher): methods = "\n".join(extract_braced(source, signature) for signature in ( "void MyMesh::writeErrFrame(", "size_t MyMesh::writePendingSerialFrame(", "void MyMesh::onContactResponse(", "bool MyMesh::onContactPathRecv(", "void MyMesh::onTraceRecv(", "void MyMesh::clearPendingReqs(", "bool MyMesh::hasPendingReqs(", "bool MyMesh::beginPendingRequest(", "bool MyMesh::allowRequestTag(", "bool MyMesh::allocateRequestTag(", "void MyMesh::armPendingRequest(", "void MyMesh::finishPendingRequest(", "void MyMesh::abandonPendingRequest(", "void MyMesh::servicePendingSerialReply(", "void MyMesh::servicePendingSerialReply(uint32_t now)", "void MyMesh::clearBinaryTraceReply(", "void MyMesh::serviceBinaryTraceReply(", "void MyMesh::serviceBinaryTraceReply(uint32_t now)", "void MyMesh::cancelSerialOperationsForRoute(", "bool MyMesh::hasFiniteDelayedReplyForRoute(", )) terminal = "\n".join(extract_braced(source, signature) for signature in ( "void MyMesh::clearTerminalLogin(", "void MyMesh::serviceTerminalLogin(", "void MyMesh::clearTerminalLogin(uint32_t now)", "void MyMesh::sendTerminalLogin(", "void MyMesh::printTerminalSendStatus(", "void MyMesh::serviceTerminalLogin(uint32_t now)", "void MyMesh::clearTerminalTrace(", "void MyMesh::serviceTerminalTrace(", "void MyMesh::clearTerminalTrace(uint32_t now)", "void MyMesh::serviceTerminalTrace(uint32_t now)", "void MyMesh::sendTerminalTraceRoute(", )) methods += "\n#if COMPANION_FEATURE_TEXT_TERMINAL\n" + terminal + "\n#endif\n" methods += "\n" + "\n".join(extract_braced(base, signature) for signature in ( "bool BaseChatMesh::onContactPathRecv(", "int BaseChatMesh::sendLogin(", "bool BaseChatMesh::allocateRequestTag(", "int BaseChatMesh::sendAnonReq(", "int BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t*", "int BaseChatMesh::sendRequest(const ContactInfo& recipient, uint8_t", )) methods += "\n" + "\n".join( extract_braced(dispatcher, signature).replace("Dispatcher::", "MyMesh::") for signature in ("bool Dispatcher::millisHasNowPassed(", "unsigned long Dispatcher::futureMillis(") ) methods += "\nnamespace mesh {\n" + "\n".join(extract_braced(packet, signature) for signature in ("bool Packet::isValidPathLen(", "size_t Packet::writePath(")) + "\n}\n" # Execute real request guards and command branches, excluding unrelated # firmware features. Packet allocation/transmission are counted boundaries. handler = extract_braced(source, "void MyMesh::handleCmdFrame(") prefix = handler[handler.index("{") + 1:handler.index(" if (cmd_frame[0] == CMD_DEVICE_QUERY")] branches = [] for command in ("LOGIN", "ANON_REQ", "STATUS_REQ", "PATH_DISCOVERY_REQ", "TELEMETRY_REQ", "BINARY_REQ", "TRACE_PATH"): block = extract_braced(source, "} else if (cmd_frame[0] == CMD_SEND_" + command) branches.append(block.replace("} else if", "if", 1) + "\n") methods += "\nvoid MyMesh::handleRequestFrame(size_t len) {\n" + prefix + "\n".join(branches) + "\n}\n" return methods def production_delayed_reply_inputs(work): source = (ROOT / "examples/companion_radio/MyMesh.cpp").read_text() base = (ROOT / "src/helpers/BaseChatMesh.cpp").read_text() packet = (ROOT / "src/Packet.cpp").read_text() dispatcher = (ROOT / "src/Dispatcher.cpp").read_text() native = (ROOT / "test/test_serial_mode_switch/test_serial_mode_switch.cpp").read_text() core = (ROOT / "src/MeshCore.h").read_text() constants = "\n".join(re.findall( r"^#define (?:PUSH_CODE_\w+|RESP_CODE_\w+|CMD_SEND_\w+|ERR_CODE_\w+|REQ_TYPE_GET_TELEMETRY_DATA)\s+.+$", source, re.MULTILINE)) wanted = ("PUB_KEY_SIZE", "MAX_PACKET_PAYLOAD", "MAX_PATH_SIZE", "OUT_PATH_UNKNOWN", "MSG_SEND_FAILED", "MSG_SEND_SENT_DIRECT", "MSG_SEND_SENT_FLOOD", "PAYLOAD_TYPE_RESPONSE", "PAYLOAD_TYPE_REQ", "PAYLOAD_TYPE_ANON_REQ", "PAYLOAD_TYPE_ACK", "REQ_TYPE_GET_STATUS", "REQ_TYPE_GET_TELEMETRY_DATA", "TELEM_PERM_BASE", "ADV_TYPE_NONE", "ADV_TYPE_ROOM", "RESP_SERVER_LOGIN_OK") for relative in ("examples/companion_radio/MyMesh.h", "src/MeshCore.h", "src/Packet.h", "src/helpers/BaseChatMesh.h", "src/helpers/ContactInfo.h", "src/helpers/AdvertDataHelpers.h", "src/helpers/SensorManager.h"): text = (ROOT / relative).read_text() constants += "\n" + "\n".join(re.findall( r"^#define (?:" + "|".join(wanted) + r")\s+.+$", text, re.MULTILINE)) constants += "\n#define EXPECTED_ACK_TABLE_SIZE 2\n" clock = "\n".join(extract_braced(core, signature) for signature in ( "uint32_t getCurrentTimeUnique()", "void resetUniqueTime(uint32_t time)")) # printf is an Arduino Print operation absent from the lean shared # host mock. Add only this I/O boundary, leaving transport logic real. stream = (ROOT / "test/mocks/Stream.h").read_text() stream = "#include \n#include \n" + stream.replace( "public:", "public:\n size_t printf(const char* format, ...) {\n" " char text[256]; va_list args; va_start(args, format);\n" " int n=vsnprintf(text,sizeof(text),format,args); va_end(args);\n" " return n>0 ? write((const uint8_t*)text, (size_t)n