Files
MeshCore/test/test_noise_floor_cli.py
T

205 lines
9.0 KiB
Python

"""Run real noise CLI dispatch, prefix handling, and room/repeater permission gates."""
from pathlib import Path
import os
import re
import shutil
import subprocess
import tempfile
import unittest
from test_replay_reset_integration import extract_braced
ROOT = Path(__file__).resolve().parents[1]
HARNESS = r'''
#include <cassert>
#include <cstring>
#include <string>
#include <vector>
#include <helpers/NoiseFloorHistory.h>
#include <helpers/TelemetryHistory.h>
#include <helpers/ExternalVoltageHistory.h>
struct ClientInfo { bool admin=true; bool isAdmin() const { return admin; } };
class Repeater {
public:
mesh::NoiseFloorHistory noise_floor_history;
void handleCommand(char* command,char* reply,ClientInfo* sender) {
@REPEATER_PREFIX@
@REPEATER_NOISE@
strcpy(reply,"Err - unrecognized command");
}
};
class MyMesh {
public:
mesh::NoiseFloorHistory noise_floor_history;
mesh::TelemetryHistory telemetry_history;
mesh::ExternalVoltageHistory external_voltage_history;
bool handleTelemetryHistoryCommand(const char*,char*,size_t=160,bool=true);
void handleCommand(uint32_t sender_timestamp,char* command,char* reply,
int client_index=-1,uint8_t path_hash_size=1) {
(void)sender_timestamp; (void)client_index; (void)path_hash_size;
@ROOM_PREFIX@
@ROOM_DISPATCH@
strcpy(reply,"Err - unrecognized command");
}
void remote(ClientInfo* client,char* text,char* reply) {
uint8_t temp[165]={};
const uint32_t sender_timestamp=100;
const int i=0;
struct Packet {uint8_t getPathHashSize() const {return 1;}} real_packet;
Packet* packet=&real_packet;
@ROOM_GATE@
strcpy(reply,reinterpret_cast<char*>(&temp[5]));
}
};
@ROOM_HISTORY@
static void seed(mesh::NoiseFloorHistory& history) {
history.advance(999900);
for (uint32_t bucket=0;bucket<176;++bucket) {
for (uint8_t profile=0;profile<2;++profile) for (uint8_t slot=0;slot<3;++slot) {
assert(history.observe(profile,999900+bucket*900+slot*300,
profile ? -66.0f : -119.0f,bucket*3+slot));
}
history.advance(999900+(bucket+1)*900);
}
}
static std::vector<uint8_t> decode(const char* text) {
const char* alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::vector<uint8_t> result;
uint32_t bits=0;unsigned count=0;
for (;*text && *text!='=';++text) {
const char* found=strchr(alphabet,*text);assert(found);
bits=(bits<<6)|uint32_t(found-alphabet);count+=6;
if (count>=8) {count-=8;result.push_back(uint8_t(bits>>count));}
}
return result;
}
static void verify(const char* reply,unsigned profile,bool prefix) {
assert(strlen(reply)==(prefix ? 159U : 158U));
assert(!memcmp(reply,prefix ? "xy|" : "> ",prefix ? 3 : 2));
const auto bytes=decode(reply+(prefix ? 3 : 2));
assert(bytes.size()==7+110 && bytes[0]==(profile ? 0x16 : 0x15));
assert(bytes[1]==uint8_t(999900) && bytes[2]==uint8_t(999900>>8)
&& bytes[3]==uint8_t(999900>>16) && bytes[4]==uint8_t(999900>>24));
assert(bytes[5]==15 && bytes[6]==176);
for (unsigned sample=0;sample<176;++sample) {
uint8_t code=0;
for (unsigned bit=0;bit<5;++bit) {
const unsigned at=sample*5+bit;
code=uint8_t((code<<1)|((bytes[7+at/8]>>(7-at%8))&1));
}
assert(code==(profile ? 28 : 1));
}
}
template<class Invoke> static void bounded(const std::string& text,Invoke invoke,
bool success,unsigned profile,bool prefix) {
std::vector<char> storage(176,'~');
std::vector<char> command(text.begin(),text.end());command.push_back(0);
invoke(command.data(),storage.data()+8);
for (unsigned i=0;i<8;++i) assert(storage[i]=='~' && storage[168+i]=='~');
assert(memchr(storage.data()+8,0,160));
if (success) verify(storage.data()+8,profile,prefix);
else assert(strstr(storage.data()+8,"Err - "));
}
int main() {
Repeater repeater;MyMesh room;
seed(repeater.noise_floor_history);seed(room.noise_floor_history);
ClientInfo admin,denied;denied.admin=false;
for (unsigned profile:{0U,1U}) for (bool prefix:{false,true}) {
const std::string text=std::string(prefix ? "xy|" : "")
+(profile ? "get telemetry.noise2" : "get telemetry.noise");
for (const std::string& suffix:{std::string(),std::string(" 1")}) {
bounded(text+suffix,[&](char* command,char* reply) {
repeater.handleCommand(command,reply,nullptr);
},true,profile,prefix);
bounded(text+suffix,[&](char* command,char* reply) {
repeater.handleCommand(command,reply,&admin);
},true,profile,prefix);
bounded(text+suffix,[&](char* command,char* reply) {
room.handleCommand(0,command,reply);
},true,profile,prefix);
bounded(text+suffix,[&](char* command,char* reply) {
room.remote(&admin,command,reply);
},true,profile,prefix);
bounded(text+suffix,[&](char* command,char* reply) {
repeater.handleCommand(command,reply,&denied);
},false,profile,prefix);
bounded(text+suffix,[&](char* command,char* reply) {
room.remote(&denied,command,reply);
},false,profile,prefix);
}
}
for (const char* bad:{"get telemetry.noisefake","get telemetry.noise2fake",
"get telemetry.noise 0","get telemetry.noise2 6"}) {
bounded(std::string("xy|")+bad,[&](char* command,char* reply) {
repeater.handleCommand(command,reply,&admin);
},false,0,true);
bounded(std::string("xy|")+bad,[&](char* command,char* reply) {
room.remote(&admin,command,reply);
},false,0,true);
}
puts("noise CLI prefix, permission and payload checks passed");
}
'''
class NoiseFloorCLITests(unittest.TestCase):
def test_actual_prefix_dispatch_permissions_and_full_pages_fit_160_bytes(self):
repeater = (ROOT / "examples/simple_repeater/MyMesh.cpp").read_text()
room = (ROOT / "examples/simple_room_server/MyMesh.cpp").read_text()
repeater_handler = extract_braced(repeater, "void MyMesh::handleCommand(uint32_t sender_timestamp,")
room_handler = extract_braced(room, "void MyMesh::handleCommand(uint32_t sender_timestamp,")
def prefix(handler):
declaration = re.search(r"const bool prefixed_reply[^;]+;", handler).group()
return declaration + "\n" + extract_braced(handler, "if (strlen(command) > 4 && command[2] == '|')")
start = repeater_handler.index(' const char* noise_commands[]')
end = repeater_handler.index(' const char* telemetry_args', start)
receiver = extract_braced(room, "void MyMesh::onPeerDataRecv(")
gate = extract_braced(receiver[receiver.index("if (!handleClientPathCommand"):], "if (client->isAdmin())")
end_gate = receiver[receiver.index(gate) + len(gate):]
gate += " " + extract_braced(end_gate, "else")
gate = gate.replace("_cli.morseReply().beginCommand(client->id.pub_key);", "")
gate = gate.replace("_cli.morseReply().endCommand();", "")
replacements = {
"@REPEATER_PREFIX@": prefix(repeater_handler),
"@REPEATER_NOISE@": repeater_handler[start:end],
"@ROOM_PREFIX@": prefix(room_handler),
"@ROOM_DISPATCH@": re.search(
r"if \(handleTelemetryHistoryCommand\([\s\S]*?\)\) return;", room_handler).group(),
"@ROOM_GATE@": gate,
"@ROOM_HISTORY@": extract_braced(room, "bool MyMesh::handleTelemetryHistoryCommand("),
}
harness = HARNESS
for marker, value in replacements.items():
harness = harness.replace(marker, value)
flags = [] if os.name == "nt" else ["-fsanitize=address,undefined",
"-fno-sanitize-recover=all", "-fno-pie", "-no-pie"]
with tempfile.TemporaryDirectory(prefix="meshcore-noise-cli-") as directory:
directory = Path(directory)
for legacy in (False, True):
with self.subTest(old_prefix_plumbing=legacy):
cpp, binary = directory / "cli.cpp", directory / "cli"
code = harness.replace("prefixed_reply ? 157 : 160, !prefixed_reply",
"prefixed_reply ? 160 : 160, true") if legacy else harness
cpp.write_text(code, encoding="ascii")
built = subprocess.run([os.environ.get("CXX") or shutil.which("g++") or "g++",
"-std=c++17", "-Wall", "-Wextra", "-Werror", *flags,
"-I", str(ROOT / "src"), str(cpp), "-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)
if legacy:
self.assertNotEqual(checked.returncode, 0)
self.assertIn("storage[i]", checked.stderr)
else:
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
self.assertIn("noise CLI prefix, permission and payload checks passed", checked.stdout)
if __name__ == "__main__":
unittest.main()