Repair stale CI fixtures for shared help and radio controls
Allow saved radio-fast metadata in the persistence harness, follow shared raw and packed CLI help, refresh reviewed help snapshots, and update HAL, build-hook and SD helper mocks without changing firmware behavior.
This commit is contained in:
1 parent
837847f891
commit
a032d53004
15 files changed
+199
-90
No files matched your search
+69
-67
@@ -30,12 +30,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
4644,
|
||||
4636
|
||||
4907,
|
||||
4899
|
||||
],
|
||||
"output_sha256": [
|
||||
"c830a4086f88e06ec7fe21bb6f1287e78fe4e72cefd7c3999740913f9ce3831d",
|
||||
"b71c0ba723d2ff693c1ed8ce23998d977607a213dba4af688fe4db16a59e38f6"
|
||||
"f35a89cf2f1f730069729b5d977edb48b89e5062e14ead8009b5675e1d3146f8",
|
||||
"8d68da4d7f2b66a8a2ad889b0f7e01eb1a9a5432388ac6466ed3c53eebd558c1"
|
||||
]
|
||||
},
|
||||
"all-options-native0-lost1": {
|
||||
@@ -68,12 +68,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
4679,
|
||||
4671
|
||||
4942,
|
||||
4934
|
||||
],
|
||||
"output_sha256": [
|
||||
"7375ec52046c4479f32f33a1845689344eb6d9c3b29d1d0a1404d57c148eb703",
|
||||
"03401a37d0b371186885ddda541f8c011c76125f98c46b27be4f3d559fe2cd3e"
|
||||
"1931d924971295f58e9c87606a101d52f4c584a3e2a76d8aed208874c17942b1",
|
||||
"81707eb39133311da39145a722ac526997e21efe597ded614ef1818af11414dc"
|
||||
]
|
||||
},
|
||||
"all-options-native1-lost0": {
|
||||
@@ -106,12 +106,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
4729,
|
||||
4721
|
||||
4992,
|
||||
4984
|
||||
],
|
||||
"output_sha256": [
|
||||
"7c2f2ca10b445bd5a4ea9f747198dc41bf4275997b38a5af1a108edb1fada34c",
|
||||
"8841b225cf40dcad34d7de15b945384e933c668dd0d63f2abbf9449cf542af95"
|
||||
"916dfbc8915ca39d0f298d2c23dee8674605f73ede5d0f39134e791523864114",
|
||||
"8ed4ff32d2e8a6e9f3dabae7bf0d1e5497ee09d77294b8e4b5399aa43e57cee9"
|
||||
]
|
||||
},
|
||||
"all-options-native1-lost1": {
|
||||
@@ -144,12 +144,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
4764,
|
||||
4756
|
||||
5027,
|
||||
5019
|
||||
],
|
||||
"output_sha256": [
|
||||
"c141a0237099f1f63ae8768001614673d6d54914ae78d41fc4a483f031326f25",
|
||||
"d562a7dcac544c502908205b14eaa20e921c76830ba5921018c4aa19798efea8"
|
||||
"7b7caa2c93f326246bc6a31b26a4efc11cae71548c812742924259b51c8d43ae",
|
||||
"bd9c03ac867cf6ad62d3febbfa4875951902878226a67081d72f188e073a0c20"
|
||||
]
|
||||
},
|
||||
"full-native0-lost0": {
|
||||
@@ -173,12 +173,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
3214,
|
||||
3206
|
||||
3564,
|
||||
3556
|
||||
],
|
||||
"output_sha256": [
|
||||
"ef1d9548ee100678eac89f0d60a42dd356bf99127eb819beccc4a15895145b94",
|
||||
"8a3fe836f56831c9c8de86254fd8186f0ece25d0d63acf3b1c916990341a0c0a"
|
||||
"2df296daf6c3050c2390dc55afffd0c2c43b35620cd8ce1a73162202b8abf02a",
|
||||
"b4b69f1a12aebab76945cdef4c7e94078a8e9a8d3f718b68facb215570e9d0cc"
|
||||
]
|
||||
},
|
||||
"full-native0-lost1": {
|
||||
@@ -202,12 +202,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
3249,
|
||||
3241
|
||||
3599,
|
||||
3591
|
||||
],
|
||||
"output_sha256": [
|
||||
"ef5d1cd1f549568acf0b1e769bdcd68bd96855301ec8e7e053df9e9c87a03b6d",
|
||||
"17a4afab06e7e61cccde52da08ea6d939ead8c34f4ca321cabdaed3efdf9da62"
|
||||
"3105232dfa903a6c15f3ed6d6034b59b0a32ca24d6151ad48fc33de621b3277a",
|
||||
"8bd526c52eedc16c7cfdc013311634470a40fdc80035760a6accacd876cca794"
|
||||
]
|
||||
},
|
||||
"full-native1-lost0": {
|
||||
@@ -231,12 +231,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
3299,
|
||||
3291
|
||||
3649,
|
||||
3641
|
||||
],
|
||||
"output_sha256": [
|
||||
"3402723097417e800d48275bb7e4b7a8911ac57a8370a544900137938fcbde66",
|
||||
"2313fef0b2d9f3f74ab816dd6d8d27b44acb126469b3e08446544c3f0c291e77"
|
||||
"88ca44fdc7bce777e3120131c0631e981c497aa0a80849b5badca9df29c0b624",
|
||||
"64f96b9832d0f83ed5fea70ae9514cf60ae1232d1589c4e73a31ddbf246bb40d"
|
||||
]
|
||||
},
|
||||
"full-native1-lost1": {
|
||||
@@ -260,12 +260,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
3334,
|
||||
3326
|
||||
3684,
|
||||
3676
|
||||
],
|
||||
"output_sha256": [
|
||||
"71c96f2d042f38c896fe14dc6f867898bb413498dfe669d874130e1595a8e560",
|
||||
"feb8bb64cca5032c07d967f7428dafb4787fc7eb342fc398ea78bb3746b4b7b8"
|
||||
"c1db29ea27a6db989227eef74c55b2ca4e07b0909df8232842d52886e02697ce",
|
||||
"f9428924f7fc57fee248100282deeb03ba66ee1a83d11ee37c3dd3bfc9edd61d"
|
||||
]
|
||||
},
|
||||
"minimal-native0-lost0": {
|
||||
@@ -275,12 +275,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
1696,
|
||||
1688
|
||||
2046,
|
||||
2038
|
||||
],
|
||||
"output_sha256": [
|
||||
"82ed92c5c52cbd699c5b57f11006367b338cab5347f06aeccb0edf80f8b16ec2",
|
||||
"c0047e0c3eae1e9d828dabe6157ed4296af1d0c93c249aa1dda0ea5920f5ef25"
|
||||
"5571a06d789dff50aebd53777095cc8f0f74e5fd4226ed399bd25050e9e5a02e",
|
||||
"9328e546b561bc1d965f8036d73c862cfed9d5077111c0b5c8744822235740bb"
|
||||
]
|
||||
},
|
||||
"minimal-native0-lost1": {
|
||||
@@ -290,12 +290,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
1731,
|
||||
1723
|
||||
2081,
|
||||
2073
|
||||
],
|
||||
"output_sha256": [
|
||||
"de5cbd88c77ed4f30817bcd89904725c97fb49142cf048e7b90e897d646e7b6b",
|
||||
"20daa08b614642afffe00bc1c89621a020e688d79a32113da16037e3a3f4c460"
|
||||
"9ee93b7a588ce48c647b9871dc7bec5c3f307c06c29a65cb6ff66a55060247b6",
|
||||
"449353daa515ede8c6528782406b3f4a101d60bee46412aa934ea94e922ed5a5"
|
||||
]
|
||||
},
|
||||
"minimal-native1-lost0": {
|
||||
@@ -305,12 +305,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
1696,
|
||||
1688
|
||||
2046,
|
||||
2038
|
||||
],
|
||||
"output_sha256": [
|
||||
"82ed92c5c52cbd699c5b57f11006367b338cab5347f06aeccb0edf80f8b16ec2",
|
||||
"c0047e0c3eae1e9d828dabe6157ed4296af1d0c93c249aa1dda0ea5920f5ef25"
|
||||
"5571a06d789dff50aebd53777095cc8f0f74e5fd4226ed399bd25050e9e5a02e",
|
||||
"9328e546b561bc1d965f8036d73c862cfed9d5077111c0b5c8744822235740bb"
|
||||
]
|
||||
},
|
||||
"minimal-native1-lost1": {
|
||||
@@ -320,12 +320,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
1731,
|
||||
1723
|
||||
2081,
|
||||
2073
|
||||
],
|
||||
"output_sha256": [
|
||||
"de5cbd88c77ed4f30817bcd89904725c97fb49142cf048e7b90e897d646e7b6b",
|
||||
"20daa08b614642afffe00bc1c89621a020e688d79a32113da16037e3a3f4c460"
|
||||
"9ee93b7a588ce48c647b9871dc7bec5c3f307c06c29a65cb6ff66a55060247b6",
|
||||
"449353daa515ede8c6528782406b3f4a101d60bee46412aa934ea94e922ed5a5"
|
||||
]
|
||||
},
|
||||
"qspi-native0-lost0": {
|
||||
@@ -359,12 +359,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
4504,
|
||||
4496
|
||||
4767,
|
||||
4759
|
||||
],
|
||||
"output_sha256": [
|
||||
"32c607f77a4c48829a6bbd91538d0dc717f813fc32251455702913a8ffea0cdb",
|
||||
"cfb475576309abb89fcad75d7d019049ed2a5489dd325fea5bcaf0ed186c9d82"
|
||||
"35197aca1bdc0f7bf52f8b468a23f019e05f5779b6eaf0cb1e498a042685ea8d",
|
||||
"69b0ad692bb4adf99dedbc192269b5f43c0270dee6c863ec93fa333952d730ac"
|
||||
]
|
||||
},
|
||||
"qspi-native0-lost1": {
|
||||
@@ -398,12 +398,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
4539,
|
||||
4531
|
||||
4802,
|
||||
4794
|
||||
],
|
||||
"output_sha256": [
|
||||
"de162715b7175070aa2fce814b9b20dcef2a1a33ca1a14a5d0063f7dbccb489a",
|
||||
"3ea7895b287cbe415d3febfc4d1f97084828f37ae4fa78e98ab8da0b0c2bf4fe"
|
||||
"6c8d2efbc41010b73ccc76cc6da9e37e1ad7f85cc38e271e982314d94f9ccbf4",
|
||||
"8f664370def3695157f7d2b56a36826182d19f8866b69643e6c654f15fc8aff6"
|
||||
]
|
||||
},
|
||||
"qspi-native1-lost0": {
|
||||
@@ -437,12 +437,12 @@
|
||||
"MESH_ENABLE_LOST_REPLY=0"
|
||||
],
|
||||
"output_bytes": [
|
||||
4589,
|
||||
4581
|
||||
4852,
|
||||
4844
|
||||
],
|
||||
"output_sha256": [
|
||||
"a173923cd67bf96c4c1f6f5d4fa933214ecaa68c8c43d7d881f53f3e49a807c5",
|
||||
"c367f8675987d86270c0b763e1249201872d0e725a20cee4b93e155d360cf12d"
|
||||
"d5f33dd01c217329a8c8833b52fef66d7f8cb36d8318099b2365e541d0c2d20d",
|
||||
"7e74ea47a40bb08a968edd6b9164901e8611ed87581df2826e10c62dc8f1c418"
|
||||
]
|
||||
},
|
||||
"qspi-native1-lost1": {
|
||||
@@ -476,17 +476,19 @@
|
||||
"MESH_ENABLE_LOST_REPLY=1"
|
||||
],
|
||||
"output_bytes": [
|
||||
4624,
|
||||
4616
|
||||
4887,
|
||||
4879
|
||||
],
|
||||
"output_sha256": [
|
||||
"9ba51362d80560bafd41f170e4791542dea7f128cca2c07c8e13413a42e74b09",
|
||||
"d3a7f18a0f799b95b3a44cee602fc3d6e60a314bdd15e5bcf4f29ad510b45cda"
|
||||
"8c718f17d64ee474727fe90ab49a3c829bb040e24b9d1f3164034d1ed85b4a4b",
|
||||
"f46636f6b6aa8a8f0104f84d1ddffb689ea56fd42d7294b209222ba76f75dd2d"
|
||||
]
|
||||
}
|
||||
},
|
||||
"schema": 1,
|
||||
"scope": "Exact existing help bytes before bounded print-call splitting; network then USB terminal mode",
|
||||
"source_commit": "c46defaefd642973cbbdb473815d56b5b261cb2e",
|
||||
"source_file_sha256": "4665bf3d9c60abe3961aa3548d51c0d3a242d49a2ff061a4732fa07d06ae5a6a"
|
||||
"scope": "Exact current shared help bytes for all 16 logging/feature profiles; network then USB terminal mode",
|
||||
"source_commit": "837847f8910221d190578f68b2e09599be2202a0",
|
||||
"source_file_sha256": "91760ca4450d42c236f4591aba4f273b4deff2d584126d6207c1561e8fd85dcd",
|
||||
"source_fragment_file": "examples/companion_radio/CompanionHelp.inc",
|
||||
"source_fragment_sha256": "f481cbb032578e425ac820f43cf91926ae1c97e609a4d0e3092377c48cbe2676"
|
||||
}
|
||||
@@ -203,6 +203,7 @@ class CLISettingsContractTest(unittest.TestCase):
|
||||
shared = extract_braced(companion, 'static bool isCompanionRadioPrefsCommand(')
|
||||
sets |= {k for verb, k in re.findall(r'"(get|set) ([\w.]+) ?"', shared) if verb == 'set'}
|
||||
self.assertEqual(gets - sets, {
|
||||
'bootloader.ver', # Installed bootloader identity
|
||||
'contact.cache', 'contact.cache.timing', # Contact-storage diagnostics
|
||||
'display.wifi', # Connection/display status
|
||||
'wifi.tcp', # Read-only app listener/traffic diagnostic
|
||||
|
||||
@@ -19,6 +19,22 @@ HARNESS = r'''
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Identity.h>
|
||||
#include <helpers/CompanionPowerDiagnostics.h>
|
||||
|
||||
// Keep the production diagnostics delegate in the command path. Identity
|
||||
// requests must not consult board power state or trigger a hardware query.
|
||||
struct IdentityBoard {
|
||||
bool hasUsbPowerDetect(){assert(false);return false;}
|
||||
bool isUsbPowered(){assert(false);return false;}
|
||||
bool hasSolarChargerDetect(){assert(false);return false;}
|
||||
bool isSolarChargerActive(){assert(false);return false;}
|
||||
uint16_t getBattMilliVolts(){assert(false);return 0;}
|
||||
bool isExternalPowered(){assert(false);return false;}
|
||||
bool isChargerActive(){assert(false);return false;}
|
||||
bool supportsPowerManagement(){assert(false);return false;}
|
||||
uint16_t getBootVoltage(){assert(false);return 0;}
|
||||
bool getSystemWatchdogRunning(bool&){assert(false);return false;}
|
||||
} board;
|
||||
|
||||
namespace mesh {
|
||||
@HEX_CHARS@
|
||||
@@ -184,7 +200,13 @@ class CompanionIdentityCliTests(unittest.TestCase):
|
||||
terminal = method(source, 'void MyMesh::handleTerminalCommand(')
|
||||
self.assertIn('handleLocalControlCommand(command, local_reply, sizeof(local_reply))', terminal)
|
||||
self.assertIn('terminalOutput().printf(" -> %s\\r\\n", local_reply);', terminal)
|
||||
self.assertIn('" get public.key\\r\\n"', terminal)
|
||||
help_branch = method(terminal, 'else if (strcmp(command, "help") == 0)')
|
||||
self.assertIn('#include "CompanionHelp.inc"', help_branch)
|
||||
self.assertIn('mesh::companion::writePackedCompanionHelp(output);', help_branch)
|
||||
help_fragments = (ROOT / 'examples/companion_radio/CompanionHelp.inc').read_text()
|
||||
self.assertIn('" get public.key\\r\\n"', help_fragments)
|
||||
packed_help = (ROOT / 'examples/companion_radio/CompanionHelpPacked.cpp').read_text()
|
||||
self.assertIn('#include "CompanionHelp.inc"', packed_help)
|
||||
shared = method(source, 'bool MyMesh::handleCommand(')
|
||||
self.assertIn('if (handleLocalControlCommand(command, reply, reply_capacity)) return true;', shared)
|
||||
self.assertIn('return handleCommand(command, 0, reply);',
|
||||
|
||||
@@ -29,9 +29,19 @@ HARNESS = r'''
|
||||
static MemoryFS* profile_metadata_fs = nullptr;
|
||||
static bool profile_metadata_error = false;
|
||||
static unsigned profile_metadata_probes = 0;
|
||||
static unsigned fast_metadata_probes = 0;
|
||||
static bool isProfileMetadataPath(const char* path) {
|
||||
for (const char* expected : {"/spiffs/radio_profiles", "/spiffs/radio_profiles.bak",
|
||||
"/spiffs/radio_profiles.tmp", "/spiffs/radio_profiles.reply",
|
||||
"/spiffs/radio_fast", "/spiffs/radio_fast.bak", "/spiffs/radio_fast.tmp"}) {
|
||||
if (!strcmp(path, expected)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int stat(const char* path, struct stat* info) {
|
||||
assert(profile_metadata_fs && !strncmp(path, "/spiffs/radio_profiles", 22));
|
||||
assert(profile_metadata_fs && isProfileMetadataPath(path));
|
||||
++profile_metadata_probes;
|
||||
if (!strncmp(path, "/spiffs/radio_fast", 18)) ++fast_metadata_probes;
|
||||
if (profile_metadata_error) { errno = EIO; return -1; }
|
||||
if (!profile_metadata_fs->exists(path + 7)) { errno = ENOENT; return -1; }
|
||||
info->st_mode = 0100000;
|
||||
@@ -131,6 +141,15 @@ struct Infrastructure {
|
||||
static bool error(const char* reply) { return strstr(reply,"Error") || strstr(reply,"ERROR"); }
|
||||
int main() {
|
||||
unsigned checks=0;
|
||||
// The shared CLI probes both stores at startup. Match only their actual
|
||||
// committed, rollback and staging names, rather than accepting any prefix.
|
||||
for (const char* path : {"/spiffs/radio_profiles", "/spiffs/radio_profiles.bak",
|
||||
"/spiffs/radio_profiles.tmp", "/spiffs/radio_profiles.reply",
|
||||
"/spiffs/radio_fast", "/spiffs/radio_fast.bak", "/spiffs/radio_fast.tmp"})
|
||||
assert(isProfileMetadataPath(path));
|
||||
for (const char* path : {"/spiffs/radio_profiles_extra", "/spiffs/radio_fast_extra",
|
||||
"/spiffs/radio_fast.reply", "/radio_profiles", "/spiffs/new_prefs"})
|
||||
assert(!isProfileMetadataPath(path));
|
||||
// App radio settings require exactly the upstream four-field CSV, even
|
||||
// with an explicit preamble or an active temporary/secondary profile.
|
||||
for(uint32_t timestamp:{0U,1700000000U})for(uint16_t preamble:{0U,48U}) {
|
||||
@@ -224,6 +243,7 @@ int main() {
|
||||
m.expectOriginal();++checks;
|
||||
}
|
||||
assert(profile_metadata_probes>0);
|
||||
assert(fast_metadata_probes>0);
|
||||
printf("%u production primary-radio persistence scenarios passed\n",checks);
|
||||
}
|
||||
'''
|
||||
|
||||
@@ -83,9 +83,15 @@ assert re.search(
|
||||
assert terminal.index("handleLocalControlCommand(") < terminal.index(
|
||||
'strncmp(command, "set ", 4) == 0'
|
||||
)
|
||||
# Help is emitted in bounded chunks with concatenated C++ literals. Check the
|
||||
# actual help branch rather than requiring one print() call per command.
|
||||
# Both direct and packed help use the same feature-gated fragments. Follow
|
||||
# the actual terminal routes before checking their shared command listings.
|
||||
help_branch = body(terminal, 'if (strcmp(command, "help") == 0)')
|
||||
assert '#include "CompanionHelp.inc"' in help_branch
|
||||
assert '#define MESH_COMPANION_HELP_BEGIN output.print(' in help_branch
|
||||
assert 'mesh::companion::writePackedCompanionHelp(output);' in help_branch
|
||||
packed_help = (root / "examples/companion_radio/CompanionHelpPacked.cpp").read_text()
|
||||
assert '#include "CompanionHelp.inc"' in packed_help
|
||||
help_fragments = (root / "examples/companion_radio/CompanionHelp.inc").read_text()
|
||||
for help_line in (' board\\r\\n', ' version\\r\\n',
|
||||
' get pwrmgt.bootreason\\r\\n', ' set pin <0-999999>\\r\\n',
|
||||
' set mt.hops <0..7> (saved; default 7)\\r\\n',
|
||||
@@ -93,14 +99,14 @@ for help_line in (' board\\r\\n', ' version\\r\\n',
|
||||
' set mt.node <!8hex|default> (saved; use a unique ID)\\r\\n',
|
||||
' set mt.shortname <1..4 UTF-8 characters|default> (saved; NodeInfo uses 4 bytes)\\r\\n',
|
||||
' send mt.nodeinfo <channel slot> (GPS-free; also automatic after first chat)\\r\\n'):
|
||||
assert help_line in help_branch
|
||||
assert help_line in help_fragments
|
||||
# Explicit NodeInfo remains available, but first successful chat also arms
|
||||
# separate GPS-free metadata. Do not restore the obsolete manual-only policy.
|
||||
assert "no automatic adverts" not in help_branch
|
||||
assert "no automatic adverts" not in help_fragments
|
||||
assert re.search(
|
||||
r"#if COMPANION_FEATURE_MEMORY_DIAGNOSTICS\s+"
|
||||
r'" memory\\r\\n"\s+#endif',
|
||||
help_branch,
|
||||
help_fragments,
|
||||
)
|
||||
|
||||
# Terminal-specific handlers keep precedence, then both the generic `set`
|
||||
|
||||
@@ -319,7 +319,8 @@ class CompanionUsbLoggingTests(unittest.TestCase):
|
||||
'-include', str(work / 'platform_shim.h'),
|
||||
'-I', str(work), '-I', str(ROOT / 'test/fixtures/radio_profiles/mocks'),
|
||||
'-I', str(ROOT / 'test/mocks'), '-I', str(ROOT / 'src'),
|
||||
'-I', str(ROOT / 'src/helpers'), '-I', str(ROOT), str(source),
|
||||
'-I', str(ROOT / 'src/helpers'),
|
||||
'-I', str(ROOT / 'examples/companion_radio'), '-I', str(ROOT), str(source),
|
||||
*[str(ROOT / 'src/helpers' / name) for name in extra],
|
||||
'-o', str(binary)], capture_output=True, text=True)
|
||||
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
|
||||
@@ -454,11 +455,12 @@ int main(){
|
||||
}
|
||||
}
|
||||
'''.replace('@HELP@', source[start:end])
|
||||
# These hashes were captured from the unmodified c46 help branch.
|
||||
# These reviewed hashes cover the production shared help fragments.
|
||||
# Compare the complete output bytes, including every conditional line,
|
||||
# rather than duplicating a second help implementation in this test.
|
||||
fixture = json.loads((ROOT / 'test/fixtures/companion_help_output.sha256.json').read_text())
|
||||
self.assertEqual(fixture['schema'], 1)
|
||||
self.assertEqual(len(fixture['profiles']), 16)
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
work = Path(directory)
|
||||
self.prepare(work)
|
||||
|
||||
@@ -123,7 +123,8 @@ assert "#elif defined(BLE_PIN_CODE)" in power_helper
|
||||
assert re.search(
|
||||
r"static bool bluetoothWiFiCoexistenceRequired\(\) \{\s+"
|
||||
r"#if defined\(COMPANION_EXCLUSIVE_WIFI_BLE\)\s+.*?return false;\s+"
|
||||
r"#elif defined\(BLE_PIN_CODE\) && defined\(WIFI_SSID\)",
|
||||
r"#elif defined\(BLE_PIN_CODE\) && \(defined\(WIFI_SSID\)"
|
||||
r" \|\| defined\(ENABLE_WIFI_INTERFACE\)\)",
|
||||
webconfig,
|
||||
re.DOTALL,
|
||||
)
|
||||
@@ -151,14 +152,20 @@ help_body = mesh[help_start:help_end]
|
||||
# bounded. Keep both policy-specific commands on that same selected stream.
|
||||
help_output = re.search(r"auto& (\w+) = terminalOutput\(\);", help_body)
|
||||
assert help_output
|
||||
assert f"#define MESH_COMPANION_HELP_BEGIN {help_output[1]}.print(" in help_body
|
||||
assert '#include "CompanionHelp.inc"' in help_body
|
||||
assert f"mesh::companion::writePackedCompanionHelp({help_output[1]});" in help_body
|
||||
help_fragments = (root / "examples/companion_radio/CompanionHelp.inc").read_text()
|
||||
packed_help = (root / "examples/companion_radio/CompanionHelpPacked.cpp").read_text()
|
||||
assert '#include "CompanionHelp.inc"' in packed_help
|
||||
help_transport = re.search(
|
||||
r"#if defined\(COMPANION_EXCLUSIVE_WIFI_BLE\)(.*?)#endif",
|
||||
help_body,
|
||||
help_fragments,
|
||||
re.DOTALL,
|
||||
)
|
||||
assert help_transport
|
||||
for text in (" get companion.transport\\r\\n", " set companion.transport <wifi|ble>\\r\\n"):
|
||||
assert f'{help_output[1]}.print("{text}")' in help_transport[1]
|
||||
assert f'"{text}"' in help_transport[1]
|
||||
assert "WebUI unavailable while Bluetooth transport is active" in mesh
|
||||
|
||||
|
||||
|
||||
@@ -1033,9 +1033,18 @@ class Nrf52ExtraFsContractTest(unittest.TestCase):
|
||||
terminal = function_body(mesh, "void MyMesh::handleTerminalCommand(")
|
||||
self.assertIn('strcmp(command, "repair extrafs")', terminal)
|
||||
self.assertIn("repairInternalExtraFS(terminalOutput());", terminal)
|
||||
self.assertIn('#include "CompanionHelp.inc"', terminal)
|
||||
self.assertIn("mesh::companion::writePackedCompanionHelp(output);", terminal)
|
||||
help_fragments = (MESH.parent / "CompanionHelp.inc").read_text(encoding="ascii")
|
||||
self.assertIn(
|
||||
"repair extrafs (erases/rebuilds internal ExtraFS)", terminal
|
||||
"repair extrafs (erases/rebuilds internal ExtraFS)", help_fragments
|
||||
)
|
||||
self.assertRegex(help_fragments,
|
||||
r"#if defined\(NRF52_PLATFORM\) && defined\(EXTRAFS\) && !defined\(QSPIFLASH\)"
|
||||
r"\s+MESH_COMPANION_HELP_BEGIN\s+"
|
||||
r'" repair extrafs \(erases/rebuilds internal ExtraFS\)\\r\\n"')
|
||||
packed_help = (MESH.parent / "CompanionHelpPacked.cpp").read_text(encoding="ascii")
|
||||
self.assertIn('#include "CompanionHelp.inc"', packed_help)
|
||||
|
||||
def test_rescue_rebuild_reports_success_only_after_every_save_succeeds(self):
|
||||
mesh = MESH.read_text(encoding="utf-8")
|
||||
|
||||
@@ -27,6 +27,7 @@ class BuildEnvironment(dict):
|
||||
self.update(changes)
|
||||
self.actions = []
|
||||
self.dependencies = []
|
||||
self.middleware = []
|
||||
|
||||
def Depends(self, target, sources):
|
||||
self.dependencies.append((target, sources))
|
||||
@@ -34,6 +35,9 @@ class BuildEnvironment(dict):
|
||||
def AddPostAction(self, target, action):
|
||||
self.actions.append((target, action))
|
||||
|
||||
def AddBuildMiddleware(self, callback, pattern):
|
||||
self.middleware.append((callback, pattern))
|
||||
|
||||
def subst(self, value):
|
||||
return (value.replace("$PROJECT_DIR", str(ROOT))
|
||||
.replace("$BUILD_DIR", str(self.get("BUILD_DIR", "build")))
|
||||
|
||||
@@ -42,11 +42,18 @@ help_body = companion[help_start:help_end]
|
||||
help_output = re.search(r"auto& (\w+) = terminalOutput\(\);", help_body)
|
||||
assert help_output
|
||||
# Bounded help writes use the cached selected stream; UF2 help stays nRF-only.
|
||||
assert '#include "CompanionHelp.inc"' in help_body
|
||||
assert (f'#define MESH_COMPANION_HELP_LINE_BEGIN {help_output[1]}.println('
|
||||
in help_body)
|
||||
assert 'mesh::companion::writePackedCompanionHelp(output);' in help_body
|
||||
help_fragments = (ROOT / "examples/companion_radio/CompanionHelp.inc").read_text()
|
||||
packed_help = (ROOT / "examples/companion_radio/CompanionHelpPacked.cpp").read_text()
|
||||
assert '#include "CompanionHelp.inc"' in packed_help
|
||||
assert re.search(
|
||||
r"#if defined\(NRF52_PLATFORM\)\s+"
|
||||
+ re.escape(f'{help_output[1]}.println(" uf2reset");')
|
||||
+ r"\s+#endif",
|
||||
help_body,
|
||||
r'MESH_COMPANION_HELP_LINE_BEGIN\s+" uf2reset"\s+'
|
||||
r"MESH_COMPANION_HELP_LINE_END\s+#endif",
|
||||
help_fragments,
|
||||
)
|
||||
|
||||
assert "mesh::cli::isUf2ResetCommand(command)" in chat
|
||||
|
||||
@@ -204,6 +204,7 @@ int main() { EnvironmentSensorManager sensors; assert(sensors.detectWisBlockStor
|
||||
methods = "namespace mesh { namespace cli {\n" + "\n".join(
|
||||
extract_braced(source, signature) for signature in (
|
||||
"static bool sdCardBusAvailable(", "static void formatSdCardElapsed(",
|
||||
"static __attribute__((noinline)) bool finishSdCardFailure(",
|
||||
"bool handleSdCardSet(", "bool handleSdCardGet(")) + "\n} }\n"
|
||||
fixture = (FIXTURES / "cli.cpp").read_text()
|
||||
build(self, fixture.replace("@METHODS@", methods), ["NRF52_PLATFORM", "OTA_SD_STORE"])
|
||||
|
||||
@@ -468,8 +468,11 @@ class Rak3401OnboardRadioTests(unittest.TestCase):
|
||||
self.assertIn("digitalWrite(SX126X_POWER_EN, HIGH);", begin)
|
||||
init = extract_braced(target, "bool radio_init()")
|
||||
self.assertIn("radio.std_init(&SPI)", init)
|
||||
self.assertIn("SX126xBufferedRadioHal radioHal(SPI)", target)
|
||||
self.assertIn("new Module(&radioHal, P_LORA_NSS, P_LORA_DIO_1, "
|
||||
# The normal nRF52 path keeps byte-wise transfers while allowing its
|
||||
# saved SPI clock selection; onboard-radio probing must not replace it.
|
||||
self.assertNotIn("BufferedRadioHal", target)
|
||||
self.assertIn("static TunableRadioHal radio_hal(SPI);", target)
|
||||
self.assertIn("new Module(&radio_hal, P_LORA_NSS, P_LORA_DIO_1, "
|
||||
"P_LORA_RESET, P_LORA_BUSY)", target)
|
||||
# External-radio construction and retry must retain their SPI1 owner.
|
||||
external_target = (VARIANT / "target.cpp").read_text()
|
||||
|
||||
@@ -109,6 +109,7 @@ class RakSdContextHandoffTests(unittest.TestCase):
|
||||
methods = 'namespace mesh { namespace cli {\n' + '\n'.join(
|
||||
extract_braced(source, signature) for signature in (
|
||||
'static bool sdCardBusAvailable(', 'static void formatSdCardElapsed(',
|
||||
'static __attribute__((noinline)) bool finishSdCardFailure(',
|
||||
'bool handleSdCardSet(')) + '\n} }\n'
|
||||
fixture = (ROOT / 'test/fixtures/rak15002_sd_seeder/maintenance.cpp').read_text()
|
||||
for profile in (
|
||||
|
||||
@@ -161,7 +161,12 @@ int main() {
|
||||
terminal = extract_braced(mesh, "void MyMesh::handleTerminalCommand(")
|
||||
self.assertIn("handleLocalControlCommand(command, local_reply, sizeof(local_reply))", terminal)
|
||||
help_body = extract_braced(terminal, 'else if (strcmp(command, "help") == 0)')
|
||||
self.assertIn('get/set display.touch <on|off> (this boot)\\r\\n', help_body)
|
||||
self.assertIn('#include "CompanionHelp.inc"', help_body)
|
||||
self.assertIn('mesh::companion::writePackedCompanionHelp(output);', help_body)
|
||||
help_fragments = (ROOT / 'examples/companion_radio/CompanionHelp.inc').read_text()
|
||||
self.assertIn('get/set display.touch <on|off> (this boot)\\r\\n', help_fragments)
|
||||
packed_help = (ROOT / 'examples/companion_radio/CompanionHelpPacked.cpp').read_text()
|
||||
self.assertIn('#include "CompanionHelp.inc"', packed_help)
|
||||
# No persistent preference schema or write is involved.
|
||||
setter = extract_braced(local, 'if (strncmp(command, "set display.touch", 17) == 0')
|
||||
self.assertNotIn("savePrefs", setter)
|
||||
|
||||
@@ -6,6 +6,8 @@ import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from test_sx126x_buffered_radio_hal import MOCK as ARDUINO_HAL_MOCK
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
IDENTITY = r"""#pragma once
|
||||
@@ -27,7 +29,9 @@ TARGET = r"""#pragma once
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
int SPI, Wire;
|
||||
#include <hal/Arduino/ArduinoHal.h>
|
||||
SPIClass SPI;
|
||||
int Wire;
|
||||
unsigned radio_bytes, prng_bytes, crypto_calls, progress_calls, init_calls, rtc_calls;
|
||||
long random(long minimum, long maximum) {
|
||||
assert(minimum == 0 && maximum == 256); ++prng_bytes; return 0x3c;
|
||||
@@ -41,12 +45,19 @@ public:
|
||||
}
|
||||
};
|
||||
@RADIO_NOISE_LISTENER@
|
||||
struct Module { Module(int, int, int, int, int&) {} };
|
||||
struct Module {
|
||||
ArduinoHal* hal;
|
||||
Module(ArduinoHal* adapter, int cs, int irq, int reset, int busy) : hal(adapter) {
|
||||
assert(cs == 4 && irq == 1 && reset == 2 && busy == 3);
|
||||
assert(hal->spi == &SPI && hal->spiSettings.hz == 8000000);
|
||||
assert(!hal->initInterface);
|
||||
}
|
||||
};
|
||||
struct FakeRadio : PhysicalLayer {
|
||||
std::unique_ptr<Module> module;
|
||||
bool next_result = false;
|
||||
FakeRadio(Module* m) : module(m) {}
|
||||
bool std_init(int* spi) {
|
||||
bool std_init(SPIClass* spi) {
|
||||
assert(spi == &SPI); ++init_calls; available = next_result; return next_result;
|
||||
}
|
||||
};
|
||||
@@ -94,11 +105,15 @@ static void freshIdentity(bool radio_ready) {
|
||||
for (uint8_t value : identity.pub_key) assert(value == expected);
|
||||
}
|
||||
int main() {
|
||||
auto* hal = TunableRadioHal::find(radio.module->hal);
|
||||
assert(hal && hal->spi == &SPI && hal->defaultHz() == 8000000);
|
||||
assert(hal->capabilities().flags == 0); // The default XIAO byte path is retained.
|
||||
freshIdentity(false); // No successful initialization has occurred yet.
|
||||
radio.next_result = true; assert(radio_init()); freshIdentity(true);
|
||||
radio.next_result = false; assert(!radio_init()); freshIdentity(false);
|
||||
radio.next_result = true; assert(radio_init()); freshIdentity(true);
|
||||
assert(init_calls == 3);
|
||||
assert(radio.module->hal == hal && hal->spi == &SPI);
|
||||
#ifdef DISABLE_I2C_RTC_SCAN
|
||||
assert(rtc_calls == 0);
|
||||
#else
|
||||
@@ -120,13 +135,17 @@ class XiaoIdentityRecoveryTests(unittest.TestCase):
|
||||
with self.subTest(crypto=crypto, rtc_scan=rtc_scan), tempfile.TemporaryDirectory(prefix="xiao-identity-") as temp:
|
||||
work = Path(temp)
|
||||
(work / "helpers").mkdir()
|
||||
(work / "hal/Arduino").mkdir(parents=True)
|
||||
(work / "hal/Arduino/ArduinoHal.h").write_text(ARDUINO_HAL_MOCK, encoding="ascii")
|
||||
(work / "Arduino.h").write_text("#pragma once\n", encoding="ascii")
|
||||
(work / "Identity.h").write_text(IDENTITY, encoding="ascii")
|
||||
(work / "helpers/ArduinoHelpers.h").write_text("#pragma once\n", encoding="ascii")
|
||||
(work / "target.h").write_text(TARGET.replace("@RADIO_NOISE_LISTENER@", listener), encoding="ascii")
|
||||
(work / "target.cpp").write_bytes((ROOT / "variants/xiao_nrf52/target.cpp").read_bytes())
|
||||
(work / "XiaoRadioSpiConfig.h").write_bytes((ROOT / "variants/xiao_nrf52/XiaoRadioSpiConfig.h").read_bytes())
|
||||
(work / "check.cpp").write_text(HARNESS, encoding="ascii")
|
||||
flags = ["-std=c++17", "-Wall", "-Wextra", "-Werror", "-I", str(work), "-I", str(ROOT / "src")]
|
||||
flags = ["-std=c++17", "-Wall", "-Wextra", "-Werror", "-DNRF52_PLATFORM=1",
|
||||
"-I", str(work), "-I", str(ROOT / "src")]
|
||||
if os.name != "nt":
|
||||
flags += ["-fsanitize=address,undefined", "-fno-pie", "-no-pie"]
|
||||
if crypto:
|
||||
|
||||
Reference in new issue
Block a user