Files
MeshCore/test/fixtures/companion_lost_reply.cpp
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

501 lines
24 KiB
C++

#include "CompanionFeatures.h"
#include "production_lost_include.inc"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <math.h>
#include <stdint.h>
#include <string>
#include <vector>
#define PUB_KEY_SIZE 32
#define MAX_HASH_SIZE 8
#define MAX_TEXT_LEN 150
#define MAX_FRAME_SIZE 172
#define MAX_GROUP_CHANNELS 40
#define EXPECTED_ACK_TABLE_SIZE 8
#define TXT_TYPE_PLAIN 0
#define TXT_TYPE_CLI_COMMAND 1
#define TXT_TYPE_SIGNED_PLAIN 2
#define ADV_TYPE_CHAT 1
#define ADV_TYPE_ROOM 3
#define OUT_PATH_UNKNOWN 255
#define MSG_SEND_FAILED 0
#define MSG_SEND_SENT_FLOOD 1
#define MSG_SEND_SENT_DIRECT 2
#define PAYLOAD_TYPE_TXT_MSG 2
#define PAYLOAD_TYPE_ACK 3
#define PAYLOAD_TYPE_RESPONSE 1
#define ONE_KEY_DM_ID_SIZE 8
#include "production_constants.inc"
namespace mesh {
struct Identity { uint8_t pub_key[PUB_KEY_SIZE] = {}; };
struct GroupChannel { uint8_t secret[16] = {}; };
struct Packet {
static bool isValidPathLen(uint8_t value) { return value != 254; }
uint8_t path_len = 0;
uint8_t path[8] = {};
uint8_t payload[5 + MAX_TEXT_LEN + 1] = {};
uint8_t recipient[PUB_KEY_SIZE] = {};
bool flood = false, used = false, do_not_retransmit = false;
uint8_t hash = 0;
void calculatePacketHash(uint8_t* out) { memset(out, hash, MAX_HASH_SIZE); }
bool isRouteFlood() const { return flood; }
bool isRouteDirect() const { return !flood; }
int getSNR() const { return 1; }
int getPathHashCount() const { return 0; }
void markDoNotRetransmit() { do_not_retransmit = true; }
};
namespace Utils {
static void sha256(uint8_t* out, size_t count, const uint8_t* a, size_t alen,
const uint8_t* b, size_t blen) {
// Hashing/encryption are the transport boundary; production composes the
// actual private payload and retains its independently matched ACK here.
uint32_t value = 2166136261U;
for (size_t i = 0; i < alen; ++i) value = (value ^ a[i]) * 16777619U;
for (size_t i = 0; i < blen; ++i) value = (value ^ b[i]) * 16777619U;
for (size_t i = 0; i < count; ++i) out[i] = uint8_t(value >> ((i % 4) * 8));
}
}
}
struct ContactInfo {
mesh::Identity id;
uint8_t type = ADV_TYPE_CHAT, out_path_len = 0, flags = 0;
bool transient = false;
uint32_t lastmod = 0;
uint8_t path[8] = {};
const uint8_t* getPath() const { return path; }
const uint8_t* getSharedSecret(const mesh::Identity&) const { return id.pub_key; }
bool isRemoteCLIAllowed() const { return flags & 0x10; }
bool setPath(const uint8_t* value, uint8_t length) {
if (!mesh::Packet::isValidPathLen(length) || length > sizeof(path)) return false;
memcpy(path, value, length); out_path_len = length; return true;
}
};
struct Clock {
uint32_t now = 0, sequence = 100;
uint32_t getMillis() const { return now; }
uint32_t getCurrentTimeUnique() { return sequence++; }
uint32_t getCurrentTime() const { return sequence; }
};
struct Manager { int free = 16; int getFreeCount() const { return free; } };
struct BaseSerialInterface {
unsigned writes = 0;
bool isConnected() const { return false; }
bool isReplyRouteAvailable(BaseSerialInterface*) const { return true; }
size_t writeFrameToRoute(BaseSerialInterface*, const uint8_t*, size_t n) { ++writes; return n; }
size_t writeFrame(const uint8_t*, size_t n) { ++writes; return n; }
};
struct UI { void setMessageNotificationOverride(bool) {} };
struct Terminal {
void print(const char*) {}
template<typename... T> void printf(const char*, T...) {}
};
struct TerminalDisplay { bool shouldShowChannel(bool) const { return false; } };
struct ChannelDetails { char name[32] = {}; };
static const uint8_t EMERGENCY_CHANNEL_SECRET[16] = {1};
struct Random { int nextInt(int, int) { return 0; } };
struct LostSnapshot { uint8_t lost_reply = 0; uint32_t capabilities = 0; };
struct WebConfigServer { static constexpr uint32_t CAP_LOST_REPLY = 1UL << 18; };
class MyMesh {
public:
struct NoMorseReply {
uint32_t generation() const { return 0; }
void beginCommand(const uint8_t*) {}
void endCommand() {}
} _morse_reply;
bool _morse_reply_admission = false;
struct Prefs { uint8_t lost_reply = 0; } _prefs;
Clock clock, *_ms = &clock;
Manager manager, *_mgr = &manager;
BaseSerialInterface serial, *_serial = &serial;
UI ui, *_ui = &ui;
mesh::Identity self_id;
#include "production_lost_state.inc"
#include "production_entry.inc"
AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE] = {};
unsigned long txt_send_timeout = 0, next_ack_expiry = 0;
bool has_next_ack_expiry = false;
uint8_t next_ack_idx = 3;
std::vector<ContactInfo> contacts;
mesh::Packet packets[16];
mesh::Packet incoming;
unsigned queued = 0, dropped = 0, sends = 0, saves = 0, cancellations = 0;
unsigned normal_marks = 0, contact_writes = 0, replacements = 0;
unsigned path_updates = 0, path_retries = 0, contact_responses = 0;
bool queue_available = true, radio_available = true, allocation_available = true;
bool save_available = true, delivered = false;
std::string last_answer;
uint8_t last_recipient[PUB_KEY_SIZE] = {};
uint8_t out_frame[MAX_FRAME_SIZE] = {};
uint8_t app_target_ver = 3;
uint16_t emergency_client_repeats[EMERGENCY_CLIENT_REPEAT_TABLE_SIZE] = {};
uint8_t emergency_client_repeat_next = 0;
mesh::Packet* emergency_client_repeat_packet = NULL;
uint16_t emergency_client_repeat_key = 0;
unsigned long emergency_client_repeat_send_at = 0;
Random random;
Terminal terminal;
TerminalDisplay _terminal_display;
uint8_t held_dm_count = 0;
unsigned offline_queue_len = 0;
struct Held { uint8_t sender_key[32] = {}, id[ONE_KEY_DM_ID_SIZE] = {}; } held_dms[1];
MyMesh() { contacts.reserve(8); }
ContactInfo& contact(uint8_t key, uint8_t suffix = 0) {
contacts.emplace_back();
contacts.back().id.pub_key[0] = key;
contacts.back().id.pub_key[31] = suffix;
return contacts.back();
}
void receive(ContactInfo& from, uint32_t timestamp, const char* question = "Am I lost?") {
onMessageRecv(from, &incoming, timestamp, question);
}
bool isTransientContact(const ContactInfo& value) const { return value.transient; }
ContactInfo* lookupPersistentContactByPubKey(const uint8_t* key, int n) {
assert(n == PUB_KEY_SIZE);
for (ContactInfo& c : contacts) if (!c.transient && memcmp(c.id.pub_key, key, n) == 0) return &c;
return NULL;
}
void markConnectionActive(const ContactInfo&) { ++normal_marks; }
void scheduleContactWrite(const ContactInfo&) { ++contact_writes; }
bool queueMessage(const ContactInfo&, uint8_t, mesh::Packet*, uint32_t,
const uint8_t*, size_t, const char*) {
if (queue_available) { ++queued; return true; } ++dropped; return false;
}
bool addToOfflineQueue(const uint8_t*, int) { ++queued; return true; }
bool savePrefs() { ++saves; return save_available; }
void makeOneKeyDMId(uint8_t* id, uint32_t timestamp, const char*) { memset(id, uint8_t(timestamp), ONE_KEY_DM_ID_SIZE); }
bool wasDeliveredOneKeyDM(const uint8_t*, const uint8_t*) { return delivered; }
void removeHeldOneKeyDM(uint8_t) { held_dm_count = 0; }
unsigned getOfflineQueueCapacity() const { return 1; }
void rememberDeliveredOneKeyDM(const uint8_t*, const uint8_t*) {}
bool hasTerminalOutput() const { return false; }
Terminal& terminalOutput() { return terminal; }
void rememberOneKeyAck(ContactInfo&) {}
int findChannelIdxForPacket(const mesh::GroupChannel&, const mesh::Packet*) { return 0; }
bool getChannel(int, ChannelDetails&) { return false; }
uint16_t emergencyClientRepeatKey(mesh::Packet*) { return 0; }
bool hasEmergencyClientRepeat(uint16_t) { return false; }
mesh::Packet* obtainNewPacket() { return NULL; }
Random* getRNG() { return &random; }
Clock* getRTCClock() { return &clock; }
unsigned long futureMillis(int millis) const { return uint32_t(clock.now + millis); }
uint32_t getTransmitAirtime(mesh::Packet*) { return 10; }
uint32_t calcFloodTimeoutMillisFor(uint32_t) { return 6000; }
uint32_t calcDirectTimeoutMillisFor(uint32_t, uint8_t) { return 3000; }
bool checkConnectionsAck(const uint8_t*, ContactInfo*& peer) { peer = NULL; return false; }
bool canMutateContacts() const { return true; }
void onContactPathUpdated(const ContactInfo&) { ++path_updates; }
void onContactResponse(const ContactInfo&, uint8_t*, uint8_t) { ++contact_responses; }
void handleReturnPathRetry(const ContactInfo&, const uint8_t*, uint8_t) { ++path_retries; }
bool hasActiveRetries(const uint8_t*) const { return false; }
void cancelActiveRetries(const uint8_t*) { ++cancellations; }
void replaceActiveRetries(mesh::Packet*, const uint8_t*) { ++replacements; }
void replaceActiveMessageRetries(mesh::Packet*, const uint8_t*, uint32_t) { ++replacements; }
mesh::Packet* createDatagram(uint8_t type, const mesh::Identity& to,
const uint8_t*, const uint8_t* data, int len) {
assert(type == PAYLOAD_TYPE_TXT_MSG);
if (!allocation_available) return NULL;
for (mesh::Packet& p : packets) if (!p.used) {
p.used = true; --manager.free;
memset(p.payload, 0, sizeof(p.payload)); memcpy(p.payload, data, len);
memcpy(p.recipient, to.pub_key, 32); p.hash = uint8_t(clock.sequence);
return &p;
}
return NULL;
}
void releasePacket(mesh::Packet* p) { if (p->used) { p->used = false; ++manager.free; } }
bool sendFloodScoped(const ContactInfo&, mesh::Packet* p, uint32_t) { return send(p); }
bool sendDirect(mesh::Packet* p, const uint8_t*, uint8_t, uint32_t) { return send(p); }
bool send(mesh::Packet* p) {
if (radio_available) {
++sends; last_answer = reinterpret_cast<const char*>(p->payload + 5);
memcpy(last_recipient, p->recipient, 32);
assert(p->payload[4] == TXT_TYPE_PLAIN);
}
releasePacket(p); return radio_available;
}
void clearExpectedAck(AckTableEntry&, bool = true);
void expireExpectedAcks();
bool processAck(const uint8_t*, ContactInfo*&);
void onAckRecv(mesh::Packet*, uint32_t);
bool onContactPathRecv(ContactInfo&, uint8_t*, uint8_t, uint8_t*, uint8_t,
uint8_t, uint8_t*, uint8_t);
void snapshot(LostSnapshot&);
void onMessageRecv(const ContactInfo&, mesh::Packet*, uint32_t, const char*);
void onCLICommandRecv(const ContactInfo&, mesh::Packet*, uint32_t, const char*, char*);
bool onSignedMessageRecv(const ContactInfo&, mesh::Packet*, uint32_t, const uint8_t*, const char*);
void onChannelMessageRecv(const mesh::GroupChannel&, mesh::Packet*, uint32_t, const char*);
bool queueChannelText(uint8_t, const uint8_t[32], float, uint8_t, uint32_t, const char*, bool);
bool handleDirectCommand(const char*, char*, size_t);
bool handleCommand(const char*, uint32_t, char*);
mesh::Packet* composeMsgPacket(const ContactInfo&, uint32_t, uint8_t, const char*, uint32_t&);
int sendMessage(const ContactInfo&, uint32_t, uint8_t, const char*, uint32_t&, uint32_t&,
uint8_t* = NULL, const uint8_t* = NULL, const uint8_t* = NULL,
uint32_t = 0);
int sendMessageDetached(const ContactInfo&, uint32_t, const char*, uint32_t&,
uint32_t&, uint8_t*);
};
#include "production_methods.inc"
#if MESH_ENABLE_LOST_REPLY
static void matchingAndOrdinaryInbox() {
for (const char* text : {"am i lost", "Am I lost?", "\t AM I LOST? \r\n", "\vam i lost\f"}) {
MyMesh m; ContactInfo& peer = m.contact(1); m._prefs.lost_reply = 2;
m.receive(peer, 7, text);
assert(m.sends == 1 && m.last_answer == "Yes" && m.queued == 1);
assert(m.last_recipient[0] == 1 && m.serial.writes == 0);
assert(m.has_next_ack_expiry && m.expected_ack_table[0].reply_route == NULL);
}
for (const char* text : {"", "a", "am i los", "am i lost??", "am i lost ?", "am i lost?",
"please am i lost?", "am i lost? yes", "am i lost\n?", "Yes", "No",
"!mail send 0123 Am I lost?", "set lost.reply yes", "am i lost\xc2\xa0"}) {
MyMesh m; auto& peer = m.contact(1); m._prefs.lost_reply = 2;
m.receive(peer, 7, text); assert(m.sends == 0 && m.queued == 1);
}
for (uint8_t setting : {uint8_t(0), uint8_t(255)}) {
MyMesh m; auto& peer = m.contact(1); m._prefs.lost_reply = setting;
m.receive(peer, 7); assert(m.sends == 0 && m.queued == 1);
}
MyMesh full; auto& peer = full.contact(1); full._prefs.lost_reply = 1;
LostSnapshot saved; full.snapshot(saved);
assert(saved.lost_reply == 1 && (saved.capabilities & WebConfigServer::CAP_LOST_REPLY));
full.queue_available = false; full.offline_queue_len = full.getOfflineQueueCapacity();
for (uint32_t question = 1; question <= 8; ++question) {
full.clock.now = question * 60000; full.receive(peer, question);
}
assert(full.sends == 8 && full.last_answer == "No" && full.dropped == 8);
#if MESH_ENABLE_ONE_KEY_DM
MyMesh held; auto& h = held.contact(2); held._prefs.lost_reply = 2;
held.queue_available = false; held.offline_queue_len = 1; held.held_dm_count = 1;
memcpy(held.held_dms[0].sender_key, h.id.pub_key, 32);
held.makeOneKeyDMId(held.held_dms[0].id, 7, "Am I lost?");
held.receive(h, 7); assert(held.sends == 1);
held.delivered = true; held.clock.now = 60000; held.receive(h, 7); assert(held.sends == 1);
#endif
}
static void admissionAndLoops() {
MyMesh m; auto& peer = m.contact(1); m._prefs.lost_reply = 2;
for (uint8_t type : {uint8_t(0), uint8_t(2), uint8_t(ADV_TYPE_ROOM)}) {
peer.type = type; m.receive(peer, 7); assert(m.sends == 0);
}
peer.type = ADV_TYPE_CHAT; peer.transient = true; m.receive(peer, 7); assert(m.sends == 0);
peer.transient = false;
ContactInfo unknown; unknown.id.pub_key[0] = 2; m.receive(unknown, 7); assert(m.sends == 0);
ContactInfo collision = peer; collision.id.pub_key[31] = 1;
m.receive(collision, 7); assert(m.sends == 0);
uint8_t prefix[4] = {}; m.onSignedMessageRecv(peer, &m.incoming, 7, prefix, "Am I lost?");
mesh::GroupChannel channel; m.onChannelMessageRecv(channel, &m.incoming, 7, "Am I lost?");
char reply[160] = {}; m.onCLICommandRecv(peer, &m.incoming, 7, "Am I lost?", reply);
peer.flags = 0x10; reply[0] = 0;
m.onCLICommandRecv(peer, &m.incoming, 7, "Am I lost?", reply);
assert(m.sends == 0);
m.receive(peer, 7); assert(m.sends == 1);
m.receive(peer, 8, "Yes"); m.receive(peer, 9, "No"); assert(m.sends == 1);
}
static void retriesCooldownAndFullKeys() {
MyMesh m; auto& one = m.contact(1); auto& same_prefix = m.contact(1, 1);
m._prefs.lost_reply = 2;
m.receive(one, 7); assert(m.sends == 1);
for (uint32_t time : {uint32_t(1), uint32_t(59999), uint32_t(60000), uint32_t(3600000)}) {
m.clock.now = time; m.receive(one, 7); assert(m.sends == 1);
}
m.clock.now = 59999; m.receive(one, 8); assert(m.sends == 1);
m.clock.now = 60000; m.receive(one, 8); assert(m.sends == 2);
m.clock.now = 69999; m.receive(same_prefix, 8); assert(m.sends == 2);
m.clock.now = 70000; m.receive(same_prefix, 8); assert(m.sends == 3 && m.last_recipient[31] == 1);
m.clock.now = 120000; m.receive(one, 9); assert(m.sends == 4);
using mesh::companion::LostReplyLimiter;
LostReplyLimiter limiter;
uint8_t keys[5][32] = {};
for (size_t i = 0; i < 5; ++i) keys[i][31] = uint8_t(i);
for (size_t i = 0; i < 4; ++i) {
uint32_t now = uint32_t(i * 10000);
assert(limiter.canReply(keys[i], 1, now)); limiter.rememberReply(keys[i], 1, now);
}
assert(!limiter.canReply(keys[4], 1, 40000));
assert(!limiter.canReply(keys[0], 2, 59999));
assert(limiter.canReply(keys[4], 1, 60000));
LostReplyLimiter wrap;
const uint32_t start = 0xfffffff0U;
wrap.rememberReply(keys[0], 1, start);
assert(!wrap.canReply(keys[0], 2, uint32_t(start + 59999U)));
assert(wrap.canReply(keys[0], 2, uint32_t(start + 60000U)));
assert(!wrap.canReply(keys[1], 1, uint32_t(start + 9999U)));
assert(wrap.canReply(keys[1], 1, uint32_t(start + 10000U)));
}
static void queueReserveUserOwnershipAndAck() {
MyMesh normal; auto& ordinary = normal.contact(3);
normal.clock.now = 100; normal.txt_send_timeout = 42;
uint32_t expected = 0, timeout = 0;
assert(normal.sendMessage(ordinary, 1, 0, "ordinary", expected, timeout) != MSG_SEND_FAILED);
assert(expected != 0 && normal.txt_send_timeout == normal.futureMillis(timeout));
MyMesh m; auto& peer = m.contact(1); m._prefs.lost_reply = 2;
auto& user = m.expected_ack_table[0];
user.ack = 10; user.expires_at = 123456; user.msg_sent = 20;
user.contact = &peer; user.reply_route = &m.serial;
user.retry_key[0] = 31; user.text_fingerprint[0] = 47;
const auto snapshot = user;
m.txt_send_timeout = 654321;
m.receive(peer, 7);
assert(m.sends == 1 && m.txt_send_timeout == 654321 && m.next_ack_idx == 3);
assert(memcmp(&user, &snapshot, sizeof(user)) == 0 && m.replacements == 0);
auto& automatic = m.expected_ack_table[1];
assert(automatic.ack != 0 && automatic.contact == &peer && automatic.reply_route == NULL);
uint32_t ack = automatic.ack; ContactInfo* matched = NULL;
assert(m.processAck(reinterpret_cast<const uint8_t*>(&ack), matched));
assert(matched == &peer && automatic.ack == 0 && m.cancellations == 1);
assert(memcmp(&user, &snapshot, sizeof(user)) == 0 && m.serial.writes == 0);
assert(!m.processAck(reinterpret_cast<const uint8_t*>(&ack), matched));
for (auto& entry : m.expected_ack_table) { entry.ack = 1; entry.expires_at = 123456; }
m.clock.now = 60000; m.receive(peer, 8); assert(m.sends == 1);
m.expected_ack_table[1].ack = 0;
for (int available : {0, 1, 2}) {
m.manager.free = available; m.receive(peer, 8); assert(m.sends == 1);
}
m.manager.free = 3; m.receive(peer, 8); assert(m.sends == 2);
assert(m.txt_send_timeout == 654321 && m.replacements == 0);
for (int failure = 0; failure < 3; ++failure) {
MyMesh f; auto& p = f.contact(1); f._prefs.lost_reply = 1; f.txt_send_timeout = 1234;
if (failure == 0) f.allocation_available = false;
if (failure == 1) f.radio_available = false;
if (failure == 2) p.out_path_len = 254;
f.receive(p, 7); assert(f.sends == 0 && f.txt_send_timeout == 1234);
assert(f.manager.free == 16 && f.expected_ack_table[0].ack == 0);
f.allocation_available = true; f.radio_available = true; p.out_path_len = OUT_PATH_UNKNOWN;
f.receive(p, 7); assert(f.sends == 1 && f.last_answer == "No");
}
}
static void automaticAckReceivePreservesUserExpiry() {
for (bool encoded_path : {false, true}) {
for (uint32_t user_deadline : {uint32_t(1000), uint32_t(9000)}) {
MyMesh m; auto& owner = m.contact(1); auto& user_peer = m.contact(2);
m.clock.now = 100; m._prefs.lost_reply = 2;
auto& user = m.expected_ack_table[0];
user.ack = 10; user.msg_sent = 20; user.expires_at = user_deadline;
user.contact = &user_peer; user.reply_route = &m.serial;
user.retry_key[0] = 31; user.text_fingerprint[0] = 47;
const auto snapshot = user;
m.txt_send_timeout = user_deadline;
m.receive(owner, 7);
auto& automatic = m.expected_ack_table[1];
const uint32_t automatic_ack = automatic.ack;
assert(automatic_ack != 0 && automatic_ack != user.ack);
assert(m.txt_send_timeout == user_deadline);
assert(m.next_ack_expiry == std::min<uint32_t>(user_deadline, automatic.expires_at));
mesh::Packet ack_packet; ack_packet.flood = true;
uint8_t ack_bytes[4], path[2] = {3, 4};
memcpy(ack_bytes, &automatic_ack, sizeof(ack_bytes));
if (encoded_path) {
assert(m.onContactPathRecv(owner, path, 2, path, 2,
PAYLOAD_TYPE_ACK, ack_bytes, sizeof(ack_bytes)));
assert(m.path_updates == 1 && owner.out_path_len == 2);
} else {
m.onAckRecv(&ack_packet, automatic_ack);
assert(ack_packet.do_not_retransmit && m.path_retries == 1);
}
// BaseChatMesh keeps its existing shared-timer ACK behavior. Companion
// owns each delivery's deadline separately and services it from loop().
assert(m.txt_send_timeout == 0 && automatic.ack == 0);
assert(memcmp(&user, &snapshot, sizeof(user)) == 0);
assert(m.has_next_ack_expiry && m.next_ack_expiry == user_deadline);
assert(m.cancellations == 1 && m.serial.writes == 0 && m.next_ack_idx == 3);
// A late duplicate cannot clear a newer shared timer or the user entry.
m.txt_send_timeout = 99999;
if (encoded_path) {
assert(m.onContactPathRecv(owner, path, 2, path, 2,
PAYLOAD_TYPE_ACK, ack_bytes, sizeof(ack_bytes)));
} else {
ack_packet.do_not_retransmit = false;
m.onAckRecv(&ack_packet, automatic_ack);
assert(!ack_packet.do_not_retransmit && m.path_retries == 1);
}
assert(m.txt_send_timeout == 99999 && m.cancellations == 1);
assert(memcmp(&user, &snapshot, sizeof(user)) == 0);
assert(m.has_next_ack_expiry && m.next_ack_expiry == user_deadline);
// Expiry still works through the actual Companion service without the
// compatibility timer being involved, including after the automatic ACK.
m.txt_send_timeout = 0;
m.clock.now = user_deadline - 1; m.expireExpectedAcks();
assert(memcmp(&user, &snapshot, sizeof(user)) == 0);
assert(m.has_next_ack_expiry && m.next_ack_expiry == user_deadline);
m.clock.now = user_deadline; m.expireExpectedAcks();
assert(user.ack == 0 && !m.has_next_ack_expiry && m.next_ack_expiry == 0);
assert(m.serial.writes == 0 && m.cancellations == 1);
}
}
}
static void localPolicyAndRemoteBoundary() {
MyMesh m; char reply[160] = {};
assert(m.handleCommand("get lost.reply", 0, reply) && std::string(reply) == "> off");
for (const char* value : {"no", "yes", "off"}) {
const std::string command = std::string("set lost.reply ") + value;
assert(m.handleCommand(command.c_str(), 0, reply));
assert(std::string(reply) == std::string("> lost.reply ") + value);
}
assert(m.saves == 3);
for (const char* invalid : {"YES", "on", "yes now", "", "1"}) {
const std::string command = std::string("set lost.reply ") + invalid;
assert(m.handleCommand(command.c_str(), 0, reply));
assert(std::string(reply).find("Error:") == 0 && m._prefs.lost_reply == 0 && m.saves == 3);
}
m.save_available = false;
assert(m.handleCommand("set lost.reply yes", 0, reply));
assert(m._prefs.lost_reply == 0 && std::string(reply).find("Error:") == 0);
m.save_available = true;
auto& peer = m.contact(1); peer.flags = 0x10;
for (uint32_t timestamp : {uint32_t(0), uint32_t(1), uint32_t(0xffffffffU)}) {
reply[0] = 0;
m.onCLICommandRecv(peer, &m.incoming, timestamp, "set lost.reply yes", reply);
assert(m._prefs.lost_reply == 0 && m.saves == 4);
reply[0] = 0;
m.onCLICommandRecv(peer, &m.incoming, timestamp, "get lost.reply", reply);
assert(std::string(reply) == "Unknown command");
}
assert(m.handleCommand("set lost.reply yes", 0, reply) && m._prefs.lost_reply == 2);
m.receive(peer, 7, "set lost.reply no"); assert(m._prefs.lost_reply == 2 && m.saves == 5 && m.sends == 0);
}
#else
// The actual gated MyMesh header fragment supplies neither runtime state nor
// a responder method in this build. The callbacks still admit normal text.
static void excludedFeature() {
MyMesh m; auto& peer = m.contact(1); m._prefs.lost_reply = 2;
for (uint32_t poll = 0; poll < 8; ++poll) {
m.clock.now = poll * 60000; m.receive(peer, poll);
}
assert(m.sends == 0 && m.queued == 8 && m.normal_marks == 8);
char reply[160] = {};
for (const char* command : {"get lost.reply", "set lost.reply yes", "set lost.reply no", "set lost.reply off"}) {
assert(!m.handleCommand(command, 0, reply));
assert(m.saves == 0 && m._prefs.lost_reply == 2 && reply[0] == 0);
}
LostSnapshot hidden; m.snapshot(hidden);
assert(hidden.lost_reply == 0 && !(hidden.capabilities & WebConfigServer::CAP_LOST_REPLY));
}
#endif
int main() {
#if MESH_ENABLE_LOST_REPLY
matchingAndOrdinaryInbox(); admissionAndLoops(); retriesCooldownAndFullKeys();
queueReserveUserOwnershipAndAck(); automaticAckReceivePreservesUserExpiry();
localPolicyAndRemoteBoundary();
#else
excludedFeature();
#endif
std::puts("PASS: Companion lost reply production paths");
}