From 5e117cb89f5cbc91d6ec2bcd0b532fb208b6939e Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 28 Oct 2026 19:53:50 -0700 Subject: [PATCH] Update capability fixtures and cover primary preamble settings --- test/fixtures/cli_settings/main.cpp | 20 +++++++++++++++++++- test/fixtures/companion_rxps_getters.cpp | 18 ++++++++++++++++++ test/fixtures/webconfig_password_command.cpp | 20 ++++++++++++++++++++ test/test_cli_settings_contract.py | 3 +++ test/test_companion_identity_cli.py | 15 +++++++++++++++ test/test_management_password_commands.py | 19 +++++++++++++++++++ 6 files changed, 94 insertions(+), 1 deletion(-) diff --git a/test/fixtures/cli_settings/main.cpp b/test/fixtures/cli_settings/main.cpp index 2969bce82..cde53e40e 100644 --- a/test/fixtures/cli_settings/main.cpp +++ b/test/fixtures/cli_settings/main.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include // Persistence has its own production-parser tests with an in-memory FS. // This fixture isolates the actual CommonCLI setting branches and callbacks. @@ -123,8 +125,9 @@ public: mesh::RadioParamApplyResult applyRxSetting(uint8_t); bool saveRxSetting(uint8_t, mesh::RadioParamApplyResult, char*); void restoreRxSetting(uint8_t, mesh::RadioParamApplyResult, uint8_t, uint32_t); - bool savePrimaryRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t) { + bool savePrimaryRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t preamble) { prefs.freq=freq; prefs.bw=bw; prefs.sf=sf; prefs.cr=cr; + prefs.primary_radio_preamble=preamble; prefs.tx_power_dbm=mesh::clampLoRaTxPower(prefs.tx_power_dbm,freq); callbacks.savePrefs(); return true; } @@ -233,6 +236,21 @@ int main() { cli.call(sender,"set radio 916,125,13,6",reply); assert(!strncmp(reply,"Error",5) && !strstr(reply,"WARN")); assert(cli._radio_profiles.warning_calls==2); + // radio.preamble reads the saved preference; the writer remains the + // optional fifth set radio field. Exercise that actual parser route. + struct Preamble { const char* input; const char* reply; uint16_t value; }; + for (const Preamble& row : {Preamble{"auto", "> auto", 0}, + {"8", "> 8", 8}, {"32", "> 32", 32}, {"88", "> 88", 88}, + {"65528", "> 65528", 65528}}) { + saves=cli.callbacks.saves; + snprintf(text,sizeof(text),"set radio 916,125,8,6,%s",row.input); + cli.call(sender,text,reply); + assert(!strncmp(reply,"OK",2) && cli.callbacks.saves==saves+1); + assert(cli.prefs.primary_radio_preamble==row.value); + cli.call(sender,"get radio.preamble",reply); + assert(!strcmp(reply,row.reply)); + assert(cli.callbacks.saves==saves+1); + } saves=cli.callbacks.saves; cli.call(sender,"set extra.sf 9,10",reply); #if defined(USE_LR2021) diff --git a/test/fixtures/companion_rxps_getters.cpp b/test/fixtures/companion_rxps_getters.cpp index 71bdd4a49..58c7676e3 100644 --- a/test/fixtures/companion_rxps_getters.cpp +++ b/test/fixtures/companion_rxps_getters.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -8,11 +9,26 @@ #include "helpers/radiolib/RXPowerSaving.h" #include "usb_mode_console.h" +// RXPS queries must not probe unrelated capability providers. Keep these +// boundaries present so the entire preceding capability branch compiles. +struct Board { + bool getSystemWatchdogRunning(bool&) { assert(false); return false; } + bool canControlLoRaFemLna() { assert(false); return false; } + bool canControlLoRaFemPaGain() { assert(false); return false; } + bool isLoRaFemPaGainEnabled() { assert(false); return false; } +} board; +struct UI { + bool supportsDisplayRotation() { assert(false); return false; } +}; +struct Profiles { + uint16_t primaryPreamble() const { assert(false); return 0; } +}; struct Radio { bool supported = true; bool fallback = false; unsigned fallback_queries = 0; bool supportsRxPowerSaving() const { return supported; } + bool supportsRxPowerSavingRfRxDisable() const { assert(false); return false; } bool isRxPowerSavingContinuousFallback() { ++fallback_queries; return fallback; } // No mutating radio APIs exist in this fixture: a write cannot compile. } radio_driver; @@ -42,6 +58,8 @@ struct Output { struct MyMesh { Prefs _prefs; + Profiles _radio_profiles; + UI* _ui = nullptr; bool temporary = false; uint8_t _temp_radio_sf = 5; float _temp_radio_bw = 500; diff --git a/test/fixtures/webconfig_password_command.cpp b/test/fixtures/webconfig_password_command.cpp index 0f1f911ad..f80e8cf02 100644 --- a/test/fixtures/webconfig_password_command.cpp +++ b/test/fixtures/webconfig_password_command.cpp @@ -15,6 +15,7 @@ HardwareSerial Serial; #include #include #include +#include #include #include namespace password = mesh::esp32::managementPassword; @@ -35,6 +36,23 @@ Debug& usbDebugPort() { static Debug value; return value; } struct StrHelper { static void strncpy(char* out, const char* in, size_t size) { ::strncpy(out, in, size); out[size - 1] = 0; }}; +// Preserve the full Companion command prefix without allowing password +// handling to consult unrelated hardware capability or profile providers. +struct Board { + bool getSystemWatchdogRunning(bool&) { assert(false); return false; } + bool canControlLoRaFemLna() { assert(false); return false; } + bool canControlLoRaFemPaGain() { assert(false); return false; } + bool isLoRaFemPaGainEnabled() { assert(false); return false; } +} board; +struct Radio { + bool supportsRxPowerSavingRfRxDisable() const { assert(false); return false; } +} radio_driver; +struct UI { + bool supportsDisplayRotation() { assert(false); return false; } +}; +struct Profiles { + uint16_t primaryPreamble() const { assert(false); return 0; } +}; struct CommonCLI { struct Prefs { char password[16] = "original"; } prefs; Prefs* _prefs = &prefs; @@ -44,6 +62,8 @@ struct CommonCLI { } }; struct MyMesh { + Profiles _radio_profiles; + UI* _ui = nullptr; char _management_password[16] = "original"; bool _management_password_ready = true; bool handleLocalControlCommand(const char*, char*, size_t); diff --git a/test/test_cli_settings_contract.py b/test/test_cli_settings_contract.py index d660eed2c..6726e3d50 100644 --- a/test/test_cli_settings_contract.py +++ b/test/test_cli_settings_contract.py @@ -59,6 +59,7 @@ COMMON_QUERY_EXCEPTIONS = { 'pwrmgt.bootreason': 'Boot/reset cause', 'pwrmgt.source': 'Measured power source', 'pwrmgt.support': 'Hardware power-management capabilities', + 'radio.preamble': 'Saved primary preference; written as the fifth set radio field', 'radio.rxps.config': 'Detailed view of set radio.rxps', 'radio.rxps.status': 'Live radio status for set radio.rxps', 'role': 'Build-selected firmware role', @@ -274,6 +275,7 @@ namespace mesh { struct Utils { 'public.key', # Read-only public identity 'pwrmgt.bootreason', 'role', # Boot/build facts 'radio.apply', # Pending/applied radio diagnostics + 'radio.preamble', # Written through the fifth set radio field 'radio.rxps.config', # Detail view of radio.rxps 'tx.status', # HIL-only applied-power verification 'wifi.status', 'wifi.ip', # Live WiFi link state @@ -319,6 +321,7 @@ namespace mesh { struct Utils { # omitted; board delegation has its own real G3 suite. set_blocks = [extract_braced(setter, 'if (strncmp(config, "path.hash.mode", 14)')] get_blocks = [extract_braced(getter, 'if (configKeyEquals(config, "path.hash.mode"))')] + get_blocks.append(extract_braced(getter, 'if (strcmp(config, "radio.preamble") == 0)')) set_blocks.append(extract_braced(setter, 'if (strcmp(config, "extra.sf") == 0 ||')) get_blocks.append(extract_braced(getter, 'if (strcmp(config, "extra.sf") == 0)')) for key in keys: diff --git a/test/test_companion_identity_cli.py b/test/test_companion_identity_cli.py index 08340814d..bb2d57d98 100644 --- a/test/test_companion_identity_cli.py +++ b/test/test_companion_identity_cli.py @@ -20,6 +20,7 @@ HARNESS = r''' #include #include #include +#include #include "usb_mode_console.h" // Keep the production diagnostics delegate in the command path. Identity @@ -35,7 +36,19 @@ struct IdentityBoard { bool supportsPowerManagement(){assert(false);return false;} uint16_t getBootVoltage(){assert(false);return 0;} bool getSystemWatchdogRunning(bool&){assert(false);return false;} + bool canControlLoRaFemLna(){assert(false);return false;} + bool canControlLoRaFemPaGain(){assert(false);return false;} + bool isLoRaFemPaGainEnabled(){assert(false);return false;} } board; +struct IdentityRadio { + bool supportsRxPowerSavingRfRxDisable(){assert(false);return false;} +} radio_driver; +struct IdentityUi { + bool supportsDisplayRotation(){assert(false);return false;} +}; +struct IdentityProfiles { + uint16_t primaryPreamble() const {assert(false);return 0;} +}; namespace mesh { @HEX_CHARS@ @@ -52,6 +65,8 @@ public: }; struct MyMesh { mesh::LocalIdentity self_id; + IdentityProfiles _radio_profiles; + IdentityUi* _ui = nullptr; unsigned fallback_calls=0; // Routing commands have their own production-method tests. This identity // harness exercises fall-through to the shared read-only key query. diff --git a/test/test_management_password_commands.py b/test/test_management_password_commands.py index 49a2b690d..53a08324e 100644 --- a/test/test_management_password_commands.py +++ b/test/test_management_password_commands.py @@ -22,6 +22,7 @@ class ManagementPasswordCommandsTest(unittest.TestCase): source = r''' #include #include +#include #include #include #include @@ -43,6 +44,22 @@ struct StrHelper { static void strncpy(char* out, const char* in, size_t size) { ::strncpy(out, in, size); out[size - 1] = 0; } }; struct NodePrefs { char password[16] = "password"; }; +// Password commands return before unrelated capability or profile queries. +struct Board { + bool getSystemWatchdogRunning(bool&) { assert(false); return false; } + bool canControlLoRaFemLna() { assert(false); return false; } + bool canControlLoRaFemPaGain() { assert(false); return false; } + bool isLoRaFemPaGainEnabled() { assert(false); return false; } +} board; +struct Radio { + bool supportsRxPowerSavingRfRxDisable() const { assert(false); return false; } +} radio_driver; +struct UI { + bool supportsDisplayRotation() { assert(false); return false; } +}; +struct Profiles { + uint16_t primaryPreamble() const { assert(false); return 0; } +}; class CommonCLI { public: NodePrefs prefs; NodePrefs* _prefs = &prefs; @@ -53,6 +70,8 @@ class CommonCLI { }; class MyMesh { public: + Profiles _radio_profiles; + UI* _ui = nullptr; char _management_password[16] = "password"; bool _management_password_ready = true; bool handleLocalControlCommand(const char*, char*, size_t);