Files
MeshCore/test/test_udp_packet_origin.py
T

97 lines
4.3 KiB
Python

#!/usr/bin/env python3
"""Exercise the UDP origin marker through real packet, pool and radio paths."""
from pathlib import Path
import os
import shutil
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class UDPPacketOriginTests(unittest.TestCase):
def test_origin_is_local_copies_with_retries_and_clears_on_reuse(self):
compiler = shutil.which("g++") or shutil.which("clang++")
self.assertIsNotNone(compiler)
fixture = (ROOT / "test/fixtures/dispatcher/startup_watchdog_test.cpp").read_text()
support = fixture[:fixture.index("static void delayedSetup()")]
support = support.replace(" using Dispatcher::setRadioAvailable;",
" using Dispatcher::setRadioAvailable;\n"
" using Dispatcher::tryParsePacket;\n"
" using Dispatcher::obtainNewPacket;")
checks = r'''
int main() {
mesh::Packet original;
original.header = (PAYLOAD_TYPE_OTA << PH_TYPE_SHIFT) | ROUTE_TYPE_FLOOD;
original.path_len = 0;
original.payload_len = 1;
original.payload[0] = 42;
uint8_t before[MAX_TRANS_UNIT], after[MAX_TRANS_UNIT];
const auto length = original.writeTo(before);
#if defined(WITH_UDP_BRIDGE)
assert(!original.udp_bridge_ingress);
original.udp_bridge_ingress = true;
mesh::Packet retry = original;
assert(retry.udp_bridge_ingress);
assert(original.writeTo(after) == length && memcmp(before, after, length) == 0);
assert(retry.readFrom(before, length) && !retry.udp_bridge_ingress);
retry.udp_bridge_ingress = true;
const uint8_t invalid[] = {0xC1, 0};
assert(!retry.readFrom(invalid, sizeof(invalid)) && !retry.udp_bridge_ingress);
StaticPoolPacketManager pool(1);
auto* packet = pool.allocNew();
assert(packet && !packet->udp_bridge_ingress);
packet->udp_bridge_ingress = true;
pool.free(packet);
auto* recycled = pool.allocNew();
assert(recycled == packet && !recycled->udp_bridge_ingress);
assert(pool.allocNew() == nullptr);
pool.free(recycled);
TestClock clock; TestRadio radio;
TestDispatcher dispatcher(radio, clock, pool);
auto* local = dispatcher.obtainNewPacket();
assert(local && !local->udp_bridge_ingress);
local->udp_bridge_ingress = true;
assert(dispatcher.tryParsePacket(local, before, length));
assert(!local->udp_bridge_ingress);
pool.free(local);
#else
assert(original.writeTo(after) == length && memcmp(before, after, length) == 0);
#endif
printf("%zu\n", sizeof(mesh::Packet));
}
'''
with tempfile.TemporaryDirectory(prefix="udp-packet-origin-") as temporary:
work = Path(temporary)
source = work / "main.cpp"
source.write_text(support + checks, encoding="ascii")
sizes = []
for enabled in (False, True):
with self.subTest(udp=enabled):
binary = work / ("udp" if enabled else "ordinary")
built = subprocess.run([
compiler, "-std=c++17", "-Wall", "-Wextra",
"-fsanitize=address,undefined", "-fno-sanitize-recover=all",
"-fno-pie", "-no-pie", *(["-DWITH_UDP_BRIDGE=1"] if enabled else []),
"-I" + str(ROOT / "test/mocks"), "-I" + str(ROOT / "src"),
str(ROOT / "src/Dispatcher.cpp"), str(ROOT / "src/Packet.cpp"),
str(ROOT / "src/helpers/StaticPoolPacketManager.cpp"),
str(source), "-o", str(binary),
], capture_output=True, text=True, timeout=60)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
environment = os.environ.copy()
# Firmware packet pools live for the entire boot.
environment["ASAN_OPTIONS"] = environment.get("ASAN_OPTIONS", "") + ":detect_leaks=0"
checked = subprocess.run([str(binary)], capture_output=True, text=True,
env=environment, timeout=10)
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
sizes.append(int(checked.stdout.strip()))
self.assertEqual(sizes[0], sizes[1], "UDP metadata must fit existing packet padding")
if __name__ == "__main__":
unittest.main()