Files
MeshCore/test/test_fleet_role_wiring.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

441 lines
18 KiB
Python

#!/usr/bin/env python3
"""Execute production role hooks around the separately tested fleet receiver."""
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
from cpp_source import body
ROOT = Path(__file__).resolve().parents[1]
HARNESS = r'''
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <SHA256.h>
#include <helpers/FleetLocation.h>
#define MESH_ENABLE_CLOCK_SYNC 0
#define MAX_TKS_ENTRIES 16
static std::vector<int> events;
namespace mesh {
struct Packet {
bool scoped = false;
uint16_t transport_codes[2] = {};
uint8_t payload[32] = {}, payload_len = 32, type = 5;
bool hasTransportCodes() const { return scoped; }
uint8_t getPayloadType() const { return type; }
};
struct Mesh {
virtual ~Mesh() = default;
virtual bool supportsFleetControl() const { return false; }
virtual bool getFleetLocation(int32_t&, int32_t&) const { return false; }
virtual void onSendComplete(Packet*) { events.push_back(1); }
virtual void onSendFail(Packet*) { events.push_back(3); }
virtual void onRadioProfileCopyQueued(Packet*, const Packet*, uint8_t) { events.push_back(5); }
};
}
struct TransportKey {
uint8_t key[16] = {};
uint16_t calcTransportCode(const mesh::Packet*) const;
};
struct Region { bool wildcard = false; bool isWildcard() const { return wildcard; } };
struct Regions {
std::vector<TransportKey> keys;
unsigned calls = 0;
int getTransportKeysFor(const Region&, TransportKey* out, int capacity) {
++calls;
const int count = std::min<int>(keys.size(), capacity);
for (int i = 0; i < count; ++i) out[i] = keys[i];
return count;
}
};
@SCOPE_HELPERS@
struct CachedSensors {
bool valid = false;
double latitude = 45.5152, longitude = -122.6784;
mutable unsigned reads = 0;
bool getCachedGpsPosition(double& lat, double& lon) const {
++reads; lat = latitude; lon = longitude; return valid;
}
};
struct Profiles {};
struct Fleet {
unsigned received = 0, serviced = 0;
bool scoped = false;
TransportKey reply_scope;
uint32_t sequence = 1735689610;
char command[80] = "set radio2 off", reply[160] = {};
void receive(mesh::Packet*, mesh::Mesh&, const TransportKey* scope) {
++received; scoped = scope != nullptr;
if (scope) reply_scope = *scope;
}
void complete(mesh::Packet*, Profiles&) { events.push_back(2); }
void fail(mesh::Packet*, Profiles&) { events.push_back(4); }
void copy(mesh::Packet*, const mesh::Packet*) { events.push_back(6); }
template<typename Handler>
void service(mesh::Mesh&, Profiles&, const char* name, Handler handler) {
assert(!strcmp(name, "fleet node")); ++serviced;
handler(sequence, command, reply);
}
};
struct CLI {
Fleet fleet;
Profiles profiles;
struct NoMorseReply {
void complete(const void*) {}
void fail(const void*) {}
void copy(const void*, const void*) {}
} morse;
NoMorseReply& morseReply() { return morse; }
bool enrolled = true;
Fleet* fleetChannel() { return enrolled ? &fleet : nullptr; }
Profiles& radioProfiles() { return profiles; }
};
struct ClockSync {
unsigned observed = 0;
void observeGroupPacket(mesh::Packet*) { ++observed; }
};
class Repeater : public mesh::Mesh {
public:
CLI _cli;
bool region_load_active = false;
Regions region_map;
Region region;
Region* recv_pkt_region = &region;
struct { const char* node_name = "fleet node"; double node_lat = 0, node_lon = 0; } _prefs;
CachedSensors sensors;
#if MESH_ENABLE_FLEET_CONTROL
bool getFleetLocation(int32_t&, int32_t&) const override;
#endif
unsigned commands = 0;
uint32_t timestamp = 0;
@REPEATER_CAPABILITY@
void onGroupPacketRecv(mesh::Packet*);
void serviceFleet();
void handleCommand(uint32_t value, void* sender, char* command, char* reply) {
assert(sender == nullptr && !strcmp(command, "set radio2 off"));
timestamp = value; ++commands; strcpy(reply, "OK");
}
};
class Room : public mesh::Mesh {
public:
CLI _cli;
ClockSync _clock_sync;
bool region_load_active = false;
Regions region_map;
Region region;
Region* recv_pkt_region = &region;
struct { const char* node_name = "fleet node"; double node_lat = 0, node_lon = 0; } _prefs;
CachedSensors sensors;
#if MESH_ENABLE_FLEET_CONTROL
bool getFleetLocation(int32_t&, int32_t&) const override;
#endif
unsigned commands = 0;
uint32_t timestamp = 0;
@ROOM_CAPABILITY@
void onGroupPacketRecv(mesh::Packet*);
void serviceFleet();
#if MESH_ENABLE_FLEET_CONTROL
void onSendComplete(mesh::Packet*) override;
void onSendFail(mesh::Packet*) override;
void onRadioProfileCopyQueued(mesh::Packet*, const mesh::Packet*, uint8_t) override;
#endif
void handleCommand(uint32_t value, char* command, char* reply) {
assert(!strcmp(command, "set radio2 off"));
timestamp = value; ++commands; strcpy(reply, "OK");
}
};
@METHODS@
template<typename Role>
void checkRole() {
Role role;
int32_t latitude = 0, longitude = 0;
assert(!role.getFleetLocation(latitude, longitude));
#if MESH_ENABLE_FLEET_CONTROL
assert(latitude == 0 && longitude == 0 && role.sensors.reads == 1);
role.sensors.valid = true;
role._prefs.node_lat = 47.6062; role._prefs.node_lon = -122.3321;
assert(role.getFleetLocation(latitude, longitude));
assert(latitude == 47606200 && longitude == -122332100 && role.sensors.reads == 1);
role._prefs.node_lat = role._prefs.node_lon = 0;
assert(role.getFleetLocation(latitude, longitude));
assert(latitude == 45515200 && longitude == -122678400 && role.sensors.reads == 2);
#else
assert(role.sensors.reads == 0);
#endif
mesh::Packet packet;
TransportKey original, rotated;
memset(original.key, 0x11, sizeof(original.key));
memset(rotated.key, 0x22, sizeof(rotated.key));
role.region_map.keys = {original, rotated};
packet.scoped = true;
packet.transport_codes[0] = rotated.calcTransportCode(&packet);
assert(role.supportsFleetControl());
role.onGroupPacketRecv(&packet);
role.serviceFleet();
#if MESH_ENABLE_FLEET_CONTROL
assert(role._cli.fleet.received == 1 && role._cli.fleet.serviced == 1);
assert(role._cli.fleet.scoped);
assert(!memcmp(role._cli.fleet.reply_scope.key, rotated.key, sizeof(rotated.key)));
assert(role.commands == 1 && role.timestamp == role._cli.fleet.sequence);
role.region_load_active = true;
role.serviceFleet();
assert(role.commands == 1);
assert(strstr(role._cli.fleet.reply, "region load active"));
#else
assert(role._cli.fleet.received == 0 && role._cli.fleet.serviced == 0);
assert(role.commands == 0);
#endif
role._cli.enrolled = false;
const unsigned before = role._cli.fleet.serviced;
role.onGroupPacketRecv(&packet);
role.serviceFleet();
assert(role._cli.fleet.serviced == before);
}
static void checkScopeSelection() {
Regions regions;
Region region;
mesh::Packet packet;
TransportKey first, rotated, selected;
memset(first.key, 0x31, sizeof(first.key));
memset(rotated.key, 0xA4, sizeof(rotated.key));
regions.keys = {first, rotated};
assert(!mesh::captureFleetReplyScope(regions, &region, &packet, selected));
assert(regions.calls == 0);
packet.scoped = true;
packet.payload[0] = 0x6D;
packet.transport_codes[1] = rotated.calcTransportCode(&packet);
assert(mesh::captureFleetReplyScope(regions, &region, &packet, selected));
assert(!memcmp(selected.key, rotated.key, sizeof(rotated.key)));
// Codes belong to the received payload. Changing it invalidates the match.
packet.payload[0] ^= 1;
assert(!mesh::captureFleetReplyScope(regions, &region, &packet, selected));
packet.payload[0] ^= 1;
packet.transport_codes[0] = first.calcTransportCode(&packet);
packet.transport_codes[1] = 0;
assert(mesh::captureFleetReplyScope(regions, &region, &packet, selected));
assert(!memcmp(selected.key, first.key, sizeof(first.key)));
region.wildcard = true;
const unsigned calls = regions.calls;
assert(!mesh::captureFleetReplyScope(regions, &region, &packet, selected));
assert(!mesh::captureFleetReplyScope(regions, (const Region*)nullptr, &packet, selected));
assert(!mesh::captureFleetReplyScope(regions, &region, nullptr, selected));
assert(regions.calls == calls);
}
int main() {
checkScopeSelection();
checkRole<Repeater>(); checkRole<Room>();
Room room; mesh::Packet first, copy;
room.onGroupPacketRecv(&first);
assert(room._clock_sync.observed == 1); // Existing clock delivery survives.
room.onSendComplete(&first);
room.onSendFail(&first);
room.onRadioProfileCopyQueued(&copy, &first, 7);
#if MESH_ENABLE_FLEET_CONTROL
assert((events == std::vector<int>{1,2,3,4,5,6}));
#else
assert((events == std::vector<int>{1,3,5}));
#endif
puts("Fleet role receive, deferred dispatch, busy guard and callbacks passed");
}
'''
AUTHORIZATION = r'''
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <new>
#include <initializer_list>
static unsigned configurations = 0, allocations = 0;
struct RegionMap {};
static RegionMap* enrolled_regions = nullptr;
struct FS { bool present = false; bool exists(const char*) const { return present; } };
namespace mesh {
struct FleetChannel {
explicit FleetChannel(void*, void* = nullptr, RegionMap* regions = nullptr) {
assert(regions); enrolled_regions = regions; ++allocations;
}
bool handleConfig(const char*, char* reply, size_t capacity) {
assert(capacity == 160); ++configurations; strcpy(reply, "OK"); return true;
}
};
}
struct Callbacks {
bool supported = true;
bool supportsFleetControl() const { return supported; }
};
class CommonCLI {
public:
Callbacks callbacks;
Callbacks* _callbacks = &callbacks;
void* _management_fs = nullptr;
RegionMap regions;
RegionMap* _region_map = &regions;
mesh::FleetChannel* _fleet_channel = nullptr;
~CommonCLI() { delete _fleet_channel; }
bool handleFleetCommand(const char*, char*);
void loadExisting(FS* fs) { @BOOT_FLEET@ }
};
struct ClientInfo {
enum Role { Guest, Admin, RegionManager, FilterManager } role;
bool isAdmin() const { return role == Admin; }
bool isRegionMgr() const { return role == RegionManager; }
bool isFilterMgr() const { return role == FilterManager; }
};
@MANAGER_HELPERS@
@COMMON_FLEET@
static void dispatch(CommonCLI& cli, ClientInfo* sender, const char* command, char* reply) {
@MANAGER_GUARD@
if (!cli.handleFleetCommand(command, reply)) strcpy(reply, "unhandled");
}
int main() {
CommonCLI cli;
char reply[160] = {};
ClientInfo admin{ClientInfo::Admin};
for (const char* command : {"get fleet.channel", "get fleet.controller", "get fleet.stats",
"set fleet.channel off", "set fleet.controller 010203"}) {
for (auto role : {ClientInfo::Guest, ClientInfo::RegionManager, ClientInfo::FilterManager}) {
ClientInfo sender{role};
const unsigned before = configurations;
dispatch(cli, &sender, command, reply);
assert(!strcmp(reply, "Err - not permitted"));
assert(configurations == before);
assert(!isRegionMgrAllowed(command) && !isFilterMgrAllowed(command));
}
dispatch(cli, &admin, command, reply);
assert(!strcmp(reply, "OK"));
dispatch(cli, nullptr, command, reply);
assert(!strcmp(reply, "OK"));
}
assert(allocations == 1 && configurations == 10 && enrolled_regions == cli._region_map);
assert(isFilterMgrAllowed("set flood.filter.1 group-text 4"));
assert(isRegionMgrAllowed("set flood.channel.scope public #local"));
assert(!isFilterMgrAllowed("set radio2 off"));
assert(!isRegionMgrAllowed("get password"));
CommonCLI unsupported;
unsupported.callbacks.supported = false;
assert(unsupported.handleFleetCommand("set fleet.channel off", reply));
assert(strstr(reply, "infrastructure roles"));
assert(allocations == 1 && configurations == 10);
assert(!cli.handleFleetCommand("set fleeter.channel off", reply));
FS disk;
CommonCLI no_existing;
no_existing.loadExisting(&disk);
assert(allocations == 1 && no_existing._fleet_channel == nullptr);
disk.present = true;
CommonCLI existing;
existing.loadExisting(&disk);
assert(allocations == 2 && existing._fleet_channel && enrolled_regions == existing._region_map);
CommonCLI disabled;
disabled.callbacks.supported = false;
disabled.loadExisting(&disk);
assert(allocations == 2 && disabled._fleet_channel == nullptr);
puts("Fleet enrollment remains restricted to local/admin infrastructure dispatch");
}
'''
class FleetRoleWiringTests(unittest.TestCase):
def test_enrollment_common_cli_stays_behind_live_manager_permissions(self):
compiler = shutil.which("g++") or shutil.which("clang++")
self.assertIsNotNone(compiler)
repeater = (ROOT / "examples/simple_repeater/MyMesh.cpp").read_text()
helpers = "\n".join(body(repeater, signature) for signature in (
"static bool commandFamilyMatches(", "static bool isCommonManagerReadOnlyAllowed(",
"static bool isRegionMgrAllowed(", "static bool isFilterMgrAllowed(",
))
handler = body(repeater, "void MyMesh::handleCommand(uint32_t sender_timestamp,")
guard_start = handler.rindex("if (sender && !sender->isAdmin())", 0,
handler.index("mesh::cli::handleACLGet("))
guard = body(handler[guard_start:], "if (sender && !sender->isAdmin())")
management = (ROOT / "src/helpers/CommonCLI_Management.cpp").read_text()
common = body(management, "bool CommonCLI::handleFleetCommand(")
boot = body(body(management, "void CommonCLI::beginManagement("),
"if (_callbacks->supportsFleetControl()")
harness = AUTHORIZATION.replace("@MANAGER_HELPERS@", helpers)
harness = harness.replace("@COMMON_FLEET@", common).replace("@MANAGER_GUARD@", guard)
harness = harness.replace("@BOOT_FLEET@", boot)
with tempfile.TemporaryDirectory(prefix="meshcore-fleet-authorization-") as directory:
work = Path(directory)
source = work / "fixture.cpp"
source.write_text(harness)
for extended_filters in (0, 1):
with self.subTest(extended_filters=extended_filters):
executable = work / str(extended_filters)
built = subprocess.run([
compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror",
"-DMESH_ENABLE_FLOOD_RULE_ENGINE=" + str(extended_filters),
str(source), "-o", str(executable),
], capture_output=True, text=True, timeout=60)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
checked = subprocess.run([str(executable)], capture_output=True,
text=True, timeout=10)
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
def test_roles_with_feature_enabled_and_disabled(self):
compiler = shutil.which("g++") or shutil.which("clang++")
self.assertIsNotNone(compiler)
methods = []
capabilities = {}
for role, folder in (("Repeater", "simple_repeater"), ("Room", "simple_room_server")):
source = (ROOT / "examples" / folder / "MyMesh.cpp").read_text()
header = (ROOT / "examples" / folder / "MyMesh.h").read_text()
capabilities[role] = body(header, "bool supportsFleetControl() const override")
methods.append(body(source, "void MyMesh::onGroupPacketRecv(").replace("MyMesh::", role + "::", 1))
methods.append("#if MESH_ENABLE_FLEET_CONTROL\n"
+ body(source, "bool MyMesh::getFleetLocation(").replace("MyMesh::", role + "::", 1)
+ "\n#endif\n")
loop = body(source, "void MyMesh::loop()")
fleet = body(loop, "if (_cli.fleetChannel())")
methods.append("void " + role + "::serviceFleet() {\n#if MESH_ENABLE_FLEET_CONTROL\n"
+ fleet + "\n#endif\n}\n")
if role == "Room":
for signature in ("void MyMesh::onSendComplete(", "void MyMesh::onSendFail(",
"void MyMesh::onRadioProfileCopyQueued("):
methods.append("#if MESH_ENABLE_FLEET_CONTROL\n"
+ body(source, signature).replace("MyMesh::", role + "::", 1)
+ "\n#endif\n")
harness = HARNESS.replace("@METHODS@", "\n".join(methods))
scope = body((ROOT / "src/helpers/FleetChannel.h").read_text(),
"template <typename Regions, typename Region>")
transport_code = body((ROOT / "src/helpers/TransportKeyStore.cpp").read_text(),
"uint16_t TransportKey::calcTransportCode(")
# The host SHA-256 shim has a byte-pointer output API; on-device Crypto
# accepts void*. Preserve the production algorithm with that cast only.
transport_code = transport_code.replace("&code, 2", "(uint8_t*)&code, 2")
harness = harness.replace("@SCOPE_HELPERS@", transport_code + "\nnamespace mesh {\n" + scope + "\n}\n")
harness = harness.replace("@REPEATER_CAPABILITY@", capabilities["Repeater"])
harness = harness.replace("@ROOM_CAPABILITY@", capabilities["Room"])
with tempfile.TemporaryDirectory(prefix="meshcore-fleet-role-") as directory:
work = Path(directory)
fixture = work / "fixture.cpp"
fixture.write_text(harness)
for enabled in (0, 1):
with self.subTest(fleet_control=enabled):
executable = work / str(enabled)
built = subprocess.run([
compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror",
"-isystem", str(ROOT / "test/mocks"), "-I", str(ROOT / "src"),
"-DMESH_ENABLE_FLEET_CONTROL=" + str(enabled),
str(fixture), "-o", str(executable),
], capture_output=True, text=True, timeout=60)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
checked = subprocess.run([str(executable)], capture_output=True,
text=True, timeout=10)
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
if __name__ == "__main__":
unittest.main()