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.
128 lines
4.9 KiB
C++
128 lines
4.9 KiB
C++
// Production JSON parser, bridge schema, and legacy import block are inserted
|
|
// by test_rs232_runtime.py. Only file access and save observation are faked.
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <helpers/ConfigSerializer.h>
|
|
#include <helpers/bridges/RS232PowerSettings.h>
|
|
|
|
struct NodePrefs : ConfigSerializer {
|
|
uint8_t bridge_enabled = 0, bridge_uart = 2, espnow_bridge_enabled = 0, rs232_bridge_enabled = 0;
|
|
uint16_t bridge_delay = 500;
|
|
uint8_t bridge_pkt_src = 1, bridge_channel = 1, bridge_format = 0;
|
|
uint32_t bridge_baud = 115200;
|
|
uint8_t usb_logging_enabled = 0, usb_debug_enabled = 0;
|
|
uint8_t rs232_autosleep = 1;
|
|
uint8_t rs232_heartbeat = 1;
|
|
uint16_t rs232_idle_seconds = 300;
|
|
char bridge_secret[16] = "test-secret", node_name[32] = "before import";
|
|
@BRIDGE_SCHEMA@
|
|
BridgePrefs bridge;
|
|
NodePrefs() : bridge(this) {}
|
|
void structure() override {
|
|
@NAME_SCHEMA@
|
|
@BRIDGE_ROOT@
|
|
}
|
|
};
|
|
struct File : Stream {
|
|
std::string text;
|
|
size_t pos = 0;
|
|
bool open = true;
|
|
explicit File(std::string value) : text(value) {}
|
|
explicit operator bool() const { return open; }
|
|
int available() override { return static_cast<int>(text.size() - pos); }
|
|
int read() override { return pos < text.size() ? text[pos++] : -1; }
|
|
int peek() override { return pos < text.size() ? text[pos] : -1; }
|
|
void close() { open = false; }
|
|
};
|
|
struct FakeFS {
|
|
std::string text;
|
|
bool exists(const char*) const { return true; }
|
|
File open(const char*, const char*) { return File(text); }
|
|
};
|
|
struct CLI {
|
|
NodePrefs prefs;
|
|
NodePrefs* _prefs = &prefs;
|
|
int saves = 0;
|
|
uint8_t saved_uart = 0, saved_enabled = 0, saved_secondary = 0;
|
|
void savePrefs(FakeFS*) {
|
|
++saves; saved_uart = prefs.bridge_uart;
|
|
saved_enabled = prefs.bridge_enabled; saved_secondary = prefs.espnow_bridge_enabled;
|
|
}
|
|
bool import(FakeFS* fs) {
|
|
bool loaded = false, is_upgrade = false;
|
|
#ifdef WITH_MQTT_BRIDGE
|
|
bool node_prefs_needs_migration = false;
|
|
#endif
|
|
@IMPORT@
|
|
assert(loaded == is_upgrade);
|
|
#ifdef WITH_MQTT_BRIDGE
|
|
assert(node_prefs_needs_migration == loaded);
|
|
#endif
|
|
return loaded;
|
|
}
|
|
};
|
|
|
|
int main() {
|
|
for (const char* json : {
|
|
"{name:\"imported node\",bridge:{en:1}}",
|
|
"{name:\"imported node\",bridge:{en:1,uart:0}}",
|
|
"{name:\"imported node\",bridge:{en:1,uart:1}}",
|
|
"{name:\"imported node\",bridge:{en:1,uart:2}}",
|
|
"{name:\"imported node\",bridge:{en:0,uart:2}}",
|
|
"{name:\"imported node\",bridge:{en:1,uart:2,rs232:1}}",
|
|
}) {
|
|
CLI cli;
|
|
cli.prefs.espnow_bridge_enabled = 1; // An old unused flag cannot prove intent.
|
|
cli.prefs.rs232_bridge_enabled = 1; // Reused objects cannot donate UART intent.
|
|
FakeFS fs{json};
|
|
assert(cli.import(&fs));
|
|
#ifdef WITH_MQTT_BRIDGE
|
|
assert(cli.saves == 0);
|
|
#else
|
|
assert(cli.saves == 1);
|
|
#endif
|
|
assert(strcmp(cli.prefs.node_name, "imported node") == 0);
|
|
#if defined(ESPNOW_BRIDGE_MERGED) && !defined(ESPNOW_BRIDGE_DEFAULT_ON) \
|
|
&& !defined(WITH_RS232_BRIDGE) && !defined(WITH_MQTT_BRIDGE)
|
|
assert(cli.prefs.bridge_enabled == 0);
|
|
#elif defined(WITH_MQTT_BRIDGE) && defined(WITH_RS232_BRIDGE)
|
|
assert(cli.prefs.bridge_enabled == (strstr(json, "en:1") != nullptr));
|
|
assert(cli.prefs.rs232_bridge_enabled == 0 && cli.prefs.bridge_uart == 2);
|
|
#elif defined(RS232_BRIDGE_MERGED) && !defined(RS232_BRIDGE_DEFAULT_ON)
|
|
const bool explicit_uart = strstr(json, "uart:2");
|
|
const bool enabled = explicit_uart && strstr(json, "en:1");
|
|
assert(cli.prefs.bridge_enabled == enabled && cli.prefs.bridge_uart == 2);
|
|
#else
|
|
assert(cli.prefs.bridge_enabled == (strstr(json, "en:1") != nullptr));
|
|
assert(cli.prefs.bridge_uart == (strstr(json, "uart:0") ? 0
|
|
: strstr(json, "uart:1") ? 1 : 2));
|
|
#endif
|
|
#if defined(ESPNOW_BRIDGE_MERGED) && !defined(ESPNOW_BRIDGE_DEFAULT_ON)
|
|
assert(cli.prefs.espnow_bridge_enabled == 0);
|
|
#elif defined(ESPNOW_BRIDGE_MERGED) && !defined(WITH_RS232_BRIDGE) && !defined(WITH_MQTT_BRIDGE)
|
|
assert(cli.prefs.espnow_bridge_enabled == cli.prefs.bridge_enabled);
|
|
#else
|
|
assert(cli.prefs.espnow_bridge_enabled == 1);
|
|
#endif
|
|
#ifndef WITH_MQTT_BRIDGE
|
|
assert(cli.saved_uart == cli.prefs.bridge_uart && cli.saved_enabled == cli.prefs.bridge_enabled);
|
|
assert(cli.saved_secondary == cli.prefs.espnow_bridge_enabled);
|
|
#endif
|
|
}
|
|
// Malformed JSON may partially apply scalar fields before the parser rejects it.
|
|
CLI broken;
|
|
broken.prefs.espnow_bridge_enabled = 1;
|
|
FakeFS malformed{"{bridge:{en:1,uart:1}"};
|
|
assert(!broken.import(&malformed) && broken.saves == 0);
|
|
#if defined(RS232_BRIDGE_MERGED) && !defined(RS232_BRIDGE_DEFAULT_ON)
|
|
assert(broken.prefs.bridge_enabled == 0 && broken.prefs.bridge_uart == 2);
|
|
#endif
|
|
#if defined(ESPNOW_BRIDGE_MERGED) && !defined(ESPNOW_BRIDGE_DEFAULT_ON)
|
|
assert(broken.prefs.espnow_bridge_enabled == 1);
|
|
#if !defined(WITH_RS232_BRIDGE) && !defined(WITH_MQTT_BRIDGE)
|
|
assert(broken.prefs.bridge_enabled == 0);
|
|
#endif
|
|
#endif
|
|
}
|