Files
MeshCore/test/test_common_prefs_apply_reply.py
T

86 lines
4.1 KiB
Python

"""Run real RS-232 apply/save branches with controlled callback outcomes.
The callbacks model application and save results, not filesystem transactions.
Actual post-publication storage failures are exercised separately by
test_common_prefs_uncertain_reply.py.
"""
from pathlib import Path
import os
import shutil
import subprocess
import sys
import tempfile
import unittest
from test_replay_reset_integration import extract_braced
ROOT = Path(__file__).resolve().parents[1]
def actual_harness(compiler, flags):
source = (ROOT / "src/helpers/CommonCLI.cpp").read_text(encoding="ascii")
# Preprocess the whole real method first: platform conditionals can
# straddle the closing brace between adjacent else-if commands.
selected = subprocess.run([
compiler, "-E", "-P", "-x", "c++", *flags, "-",
], input=extract_braced(source, "void CommonCLI::handleSetCmd("),
capture_output=True, text=True, check=True, timeout=20).stdout
branches = []
for method, signature in (
("rs232", 'if (strncmp(config, "rs232.enabled ", 14) == 0)'),
("bridge", 'if (memcmp(config, "bridge.enabled ", 15) == 0)'),
("baud", 'if (memcmp(config, "bridge.baud ", 12) == 0)'),
("uart", 'if (memcmp(config, "bridge.uart ", 12) == 0)'),
):
definition = ("void CommonCLI::" + method
+ "(const char* config, char* reply) {\n"
+ extract_braced(selected, signature) + "\n}\n")
if method == "bridge":
# The strict apply/save bridge.enabled branch is standalone UART
# only. WITH_MQTT_BRIDGE keeps its independent transport policy.
definition = "#ifndef WITH_MQTT_BRIDGE\n" + definition + "#endif\n"
branches.append(definition)
fixture = (ROOT / "test/fixtures/common_prefs_apply_reply.cpp").read_text(
encoding="ascii")
return fixture.replace("@PARSE_ON_OFF@", extract_braced(
source, "static bool parseOnOffStrict(")).replace(
"@PRODUCTION_BRANCHES@", "\n".join(branches))
class CommonPrefsApplyReplyTests(unittest.TestCase):
def test_actual_apply_save_rollback_combinations(self):
compiler = os.environ.get("CXX") or shutil.which("g++") or shutil.which("clang++")
self.assertIsNotNone(compiler, "a host C++17 compiler is required")
sanitizer_flags = (["-fsanitize=address,undefined", "-fno-sanitize-recover=all",
"-fno-pie", "-no-pie"]
if sys.platform.startswith("linux") else [])
with tempfile.TemporaryDirectory(prefix="common-prefs-apply-reply-") as directory:
work = Path(directory)
for mqtt in (False, True):
with self.subTest(mqtt=mqtt):
flags = ["-DWITH_BRIDGE=1", "-DWITH_RS232_BRIDGE=1",
"-DWITH_RS232_BRIDGE_UART=1", "-DWITH_RS232_BRIDGE_ALT=1",
"-DWITH_RS232_BRIDGE_ALT_UART=2"]
if mqtt:
flags.append("-DWITH_MQTT_BRIDGE=1")
(work / "test.cpp").write_text(actual_harness(compiler, flags),
encoding="ascii")
binary = work / ("test.exe" if os.name == "nt" else "test")
built = subprocess.run([
compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror",
*flags, *sanitizer_flags, "-I" + str(ROOT / "src"),
str(work / "test.cpp"), "-o", str(binary),
], capture_output=True, text=True, timeout=60)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
tested = subprocess.run([str(binary)], capture_output=True,
text=True, timeout=20)
self.assertEqual(tested.returncode, 0, tested.stdout + tested.stderr)
cases = 64 if mqtt else 89
self.assertIn(
f"{cases} actual CommonCLI apply/save/restore cases passed",
tested.stdout)
if __name__ == "__main__":
unittest.main()