Merge remote-tracking branch 'origin/keymindCascade' into keymindCascade
This commit is contained in:
commit
eab29a12cd
15 files changed
+2067
-166
No files matched your search
+72
-11
@@ -453,8 +453,8 @@ int main() {
|
||||
sensor = ram.requirements("NRF52_PLATFORM", {}, "t114_sensor")
|
||||
self.assertEqual(sensor["components"]["client_table"], 32 * 320 + 16)
|
||||
|
||||
def test_g3_psram_tables_reserve_full_internal_fallback_without_cutting_other_owners(self):
|
||||
flags = {"STATION_G3_ESP32": 1, "BOARD_HAS_PSRAM": 1,
|
||||
def test_s3_psram_tables_reserve_full_internal_fallback_without_cutting_other_owners(self):
|
||||
flags = {"CONFIG_IDF_TARGET_ESP32S3": 1, "BOARD_HAS_PSRAM": 1,
|
||||
"MESH_CLIENT_REPEATER_ONLY": 1, "WITH_MQTT_BRIDGE": 1,
|
||||
"DISPLAY_CLASS": "SH1106Display", "ADMIN_PASSWORD": "test",
|
||||
"ENABLE_OTA": 1, "WITH_UDP_BRIDGE": 1, "WITH_RS232_BRIDGE": "Serial2"}
|
||||
@@ -475,11 +475,11 @@ int main() {
|
||||
self.assertEqual(ram.requirements("ESP32_PLATFORM", {
|
||||
**flags, "MESH_REPEATER_PSRAM_TABLES": 1}, target), selected)
|
||||
|
||||
def test_g3_psram_table_defaults_follow_board_psram_platform_and_source_role_gates(self):
|
||||
flags = {"STATION_G3_ESP32": 1, "BOARD_HAS_PSRAM": 1,
|
||||
def test_psram_table_defaults_follow_s3_psram_platform_and_source_role_gates(self):
|
||||
flags = {"CONFIG_IDF_TARGET_ESP32S3": 1, "BOARD_HAS_PSRAM": 1,
|
||||
"MESH_CLIENT_REPEATER_ONLY": 1}
|
||||
cases = [
|
||||
("ESP32_PLATFORM", {k: v for k, v in flags.items() if k != "STATION_G3_ESP32"},
|
||||
("ESP32_PLATFORM", {k: v for k, v in flags.items() if k != "CONFIG_IDF_TARGET_ESP32S3"},
|
||||
"other_s3_repeater"),
|
||||
("ESP32_PLATFORM", {k: v for k, v in flags.items() if k != "BOARD_HAS_PSRAM"},
|
||||
"Station_G3_ESP32_repeater"),
|
||||
@@ -493,6 +493,10 @@ int main() {
|
||||
("ESP32_PLATFORM", {**flags, "MESH_CLIENT_REPEATER_ONLY": 0},
|
||||
"Station_G3_ESP32_room_server"),
|
||||
]
|
||||
for mcu in ("ESP32", "ESP32S2", "ESP32C3", "ESP32C6"):
|
||||
cases.append(("ESP32_PLATFORM", {
|
||||
**flags, "CONFIG_IDF_TARGET_ESP32S3": 0,
|
||||
"CONFIG_IDF_TARGET_" + mcu: 1}, "other_repeater"))
|
||||
for platform in ("NRF52_PLATFORM", "RP2040_PLATFORM", "STM32_PLATFORM"):
|
||||
cases.extend((platform, {**flags, **extra}, "other_repeater")
|
||||
for extra in ({}, {"MESH_REPEATER_PSRAM_TABLES": 1}))
|
||||
@@ -508,6 +512,30 @@ int main() {
|
||||
self.assertIn("repeater_scope_storage_fallback", renamed["components"])
|
||||
self.assertIn("repeater_advert_storage_fallback", renamed["components"])
|
||||
|
||||
def test_psram_repeater_table_defaults_cover_opi_and_qspi_families_without_a_g3_marker(self):
|
||||
flags = {"BOARD_HAS_PSRAM": 1, "MESH_CLIENT_REPEATER_ONLY": 1,
|
||||
"CONFIG_IDF_TARGET_ESP32S3": 1, "CONFIG_SPIRAM_USE_MALLOC": 1}
|
||||
families = (
|
||||
("LilyGo_TDeck_repeater", {"CONFIG_SPIRAM_MODE_OCT": 1,
|
||||
"CONFIG_SPIRAM_BOOT_INIT": 1}),
|
||||
("Station_G2_repeater", {"CONFIG_SPIRAM_MODE_OCT": 1,
|
||||
"CONFIG_SPIRAM_BOOT_INIT": 1}),
|
||||
("heltec_v4_repeater", {"CONFIG_SPIRAM_MODE_QUAD": 1}),
|
||||
("SenseCapIndicator-LoRa-N16R2_repeater", {"CONFIG_SPIRAM_MODE_QUAD": 1}),
|
||||
)
|
||||
for target, extra in families:
|
||||
with self.subTest(target=target):
|
||||
defines = {**flags, **extra}
|
||||
self.assertNotIn("STATION_G3_ESP32", defines)
|
||||
selected = ram.requirements("ESP32_PLATFORM", defines, target)
|
||||
original = ram.requirements("ESP32_PLATFORM", {
|
||||
**defines, "MESH_REPEATER_PSRAM_TABLES": 0}, target)
|
||||
self.assertEqual(selected["components"], {
|
||||
**original["components"], "repeater_scope_storage_fallback": 18892,
|
||||
"repeater_advert_storage_fallback": 17936})
|
||||
self.assertEqual(selected["required_heap_bytes"] - original["required_heap_bytes"],
|
||||
36828)
|
||||
|
||||
def test_explicit_psram_repeater_table_opt_in_and_capacity_overrides_are_fully_budgeted(self):
|
||||
flags = {"BOARD_HAS_PSRAM": 1, "MESH_CLIENT_REPEATER_ONLY": 1,
|
||||
"MESH_REPEATER_PSRAM_TABLES": 1}
|
||||
@@ -537,7 +565,7 @@ int main() {
|
||||
original["required_heap_bytes"] + scope + advert)
|
||||
|
||||
def test_psram_advert_budget_excludes_portable_iram_and_rtc_storage_only(self):
|
||||
flags = {"STATION_G3_ESP32": 1, "BOARD_HAS_PSRAM": 1,
|
||||
flags = {"CONFIG_IDF_TARGET_ESP32S3": 1, "BOARD_HAS_PSRAM": 1,
|
||||
"MESH_CLIENT_REPEATER_ONLY": 1}
|
||||
target = "Station_G3_ESP32_repeater"
|
||||
original = ram.requirements("ESP32_PLATFORM", flags, target)
|
||||
@@ -560,7 +588,7 @@ int main() {
|
||||
**flags, **disabled_backends}, target), original)
|
||||
|
||||
def test_each_psram_table_fallback_can_set_the_contiguous_allocation_bound(self):
|
||||
flags = {"STATION_G3_ESP32": 1, "BOARD_HAS_PSRAM": 1,
|
||||
flags = {"CONFIG_IDF_TARGET_ESP32S3": 1, "BOARD_HAS_PSRAM": 1,
|
||||
"MESH_CLIENT_REPEATER_ONLY": 1, "MAX_CLIENTS": 1,
|
||||
"FLOOD_PACKET_FILTER_SLOTS": 1, "MAX_RECENT_REPEATERS": 0}
|
||||
for extra, name in (({}, "repeater_scope_storage_fallback"),
|
||||
@@ -571,13 +599,14 @@ int main() {
|
||||
self.assertEqual(policy["required_contiguous_bytes"], policy["components"][name])
|
||||
self.assertGreater(policy["required_contiguous_bytes"], 16384)
|
||||
|
||||
def test_g3_psram_fallback_elf_gate_rejects_old_budget_and_enforces_exact_total(self):
|
||||
flags = {"STATION_G3_ESP32": 1, "BOARD_HAS_PSRAM": 1,
|
||||
def test_s3_psram_fallback_elf_gate_rejects_old_budget_and_enforces_exact_total(self):
|
||||
flags = {"BOARD_HAS_PSRAM": 1,
|
||||
"MESH_CLIENT_REPEATER_ONLY": 1, "WITH_MQTT_BRIDGE": 1}
|
||||
target = "Station_G3_ESP32_repeater"
|
||||
target = "heltec_v4_repeater"
|
||||
previous = {**flags, "MESH_REPEATER_PSRAM_TABLES": 0}
|
||||
old = ram.requirements("ESP32_PLATFORM", previous, target)["required_heap_bytes"]
|
||||
required = ram.requirements("ESP32_PLATFORM", flags, target)["required_heap_bytes"]
|
||||
required = ram.requirements("ESP32_PLATFORM", {
|
||||
**flags, "CONFIG_IDF_TARGET_ESP32S3": 1}, target)["required_heap_bytes"]
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
path = Path(temporary) / "firmware.elf"
|
||||
report = Path(temporary) / "firmware.memory.json"
|
||||
@@ -596,6 +625,38 @@ int main() {
|
||||
self.assertEqual(result["components"]["repeater_scope_storage_fallback"], 18892)
|
||||
self.assertEqual(result["components"]["repeater_advert_storage_fallback"], 17936)
|
||||
|
||||
def test_psram_table_elf_policy_uses_authoritative_mcu_over_missing_or_stale_sdk_macros(self):
|
||||
flags = {"BOARD_HAS_PSRAM": 1, "MESH_CLIENT_REPEATER_ONLY": 1}
|
||||
target = "board_repeater"
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
path = Path(temporary) / "firmware.elf"
|
||||
report = Path(temporary) / "firmware.memory.json"
|
||||
for mcu, stale_macro in (("esp32s3", None), ("esp32s3", 0),
|
||||
("esp32", None), ("esp32", 1)):
|
||||
with self.subTest(mcu=mcu, stale_macro=stale_macro), contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
|
||||
defines = dict(flags)
|
||||
if stale_macro is not None:
|
||||
defines["CONFIG_IDF_TARGET_ESP32S3"] = stale_macro
|
||||
expected = ram.requirements("ESP32_PLATFORM", {
|
||||
**defines, "CONFIG_IDF_TARGET_ESP32S3": int(mcu == "esp32s3"),
|
||||
"MESH_RAM_DEFAULT_RECENT_REPEATERS": 256 if mcu == "esp32" else 2048,
|
||||
}, target)
|
||||
capacity = expected["required_heap_bytes"]
|
||||
# Classic ESP32 reclaims the second 64 KiB ROM-stack
|
||||
# region. The S3 excludes it; both exclude the first 16 KiB.
|
||||
length = capacity + 0x4000 - (0x10000 if mcu == "esp32" else 0)
|
||||
esp_fixture(path, modern=True, internal_length=length)
|
||||
status = ram.check_firmware(path, "ESP32_PLATFORM", mcu,
|
||||
defines, target, report)
|
||||
result = json.loads(report.read_text())
|
||||
self.assertEqual(status, 0)
|
||||
self.assertTrue(result["passed"])
|
||||
self.assertEqual(result["available_internal_bytes"], capacity)
|
||||
self.assertEqual(result["required_heap_bytes"], capacity)
|
||||
self.assertEqual(result["components"], expected["components"])
|
||||
for name in ("repeater_scope_storage_fallback", "repeater_advert_storage_fallback"):
|
||||
self.assertEqual(name in result["components"], mcu == "esp32s3")
|
||||
|
||||
def test_browser_terminal_reserves_internal_session_and_psram_aware_scrollback(self):
|
||||
defines = {"ENABLE_USB_INTERFACE": 1, "WIFI_SSID": "", "DISPLAY_CLASS": "SSD1306Display"}
|
||||
base = ram.requirements("ESP32_PLATFORM", {**defines, "WEBCONFIG_DISABLED": 1}, "v4_companion")
|
||||
|
||||
@@ -115,7 +115,6 @@ class FloodAdvertPsramTests(unittest.TestCase):
|
||||
(cls.work / "esp_heap_caps.h").write_text(HEAP)
|
||||
(cls.work / "sdkconfig.h").write_text(
|
||||
"#define CONFIG_IDF_TARGET_ESP32S3 1\n"
|
||||
"#define CONFIG_SPIRAM_BOOT_INIT 1\n"
|
||||
"#define CONFIG_SPIRAM_USE_MALLOC 1\n")
|
||||
cls.compiler = [os.environ.get("CXX", "g++")]
|
||||
cls.binaries = {}
|
||||
@@ -132,6 +131,16 @@ class FloodAdvertPsramTests(unittest.TestCase):
|
||||
source = self.work / (name + ".cpp")
|
||||
binary = self.work / name
|
||||
code = HARNESS.replace("@BANKS@", "").replace("int main(", "int traceMain(")
|
||||
code = code.replace(
|
||||
"#define MAKE_LIMITER(name) mesh::StaticFloodAdvertLimiter<> name\n",
|
||||
"#define MAKE_LIMITER(name) \\\n"
|
||||
" const unsigned name##_calls = heap_test::heap().external_calls + heap_test::heap().internal_calls; \\\n"
|
||||
" mesh::StaticFloodAdvertLimiter<> name; \\\n"
|
||||
" CHECK(heap_test::heap().external_calls + heap_test::heap().internal_calls == name##_calls); \\\n"
|
||||
" CHECK(name.begin()); \\\n"
|
||||
" const unsigned name##_initialized = heap_test::heap().external_calls + heap_test::heap().internal_calls; \\\n"
|
||||
" CHECK(name.begin()); \\\n"
|
||||
" CHECK(heap_test::heap().external_calls + heap_test::heap().internal_calls == name##_initialized)\n")
|
||||
# Release builds of the limiter are exercised while harness assertions
|
||||
# stay active. The trace also checks exact full-key prefix collisions.
|
||||
code = code.rstrip()[:-1] + " return 0;\n}\n"
|
||||
|
||||
@@ -66,6 +66,10 @@ public:
|
||||
uint8_t flood_channel_data_rule_max_hops = FLOOD_CHANNEL_HOPS_ALL;
|
||||
FloodChannelScopeEntry flood_channel_scopes[FLOOD_CHANNEL_SCOPE_SLOTS]{};
|
||||
char flood_channel_direct_scopes[FLOOD_CHANNEL_DIRECT_SCOPE_SLOTS][32]{};
|
||||
auto floodChannelScopes() -> decltype(flood_channel_scopes)& { return flood_channel_scopes; }
|
||||
auto floodChannelScopes() const -> const decltype(flood_channel_scopes)& { return flood_channel_scopes; }
|
||||
auto floodChannelDirectScopes() -> decltype(flood_channel_direct_scopes)& { return flood_channel_direct_scopes; }
|
||||
auto floodChannelDirectScopes() const -> const decltype(flood_channel_direct_scopes)& { return flood_channel_direct_scopes; }
|
||||
uint8_t flood_packet_filter_blacklist_count = 0;
|
||||
uint8_t flood_packet_filter_blacklist[18][3]{};
|
||||
unsigned seeded = 0;
|
||||
|
||||
@@ -13,7 +13,6 @@ from test_flood_advert_psram import HEAP
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SDK = ("#define CONFIG_IDF_TARGET_ESP32S3 1\n"
|
||||
"#define CONFIG_SPIRAM_BOOT_INIT 1\n"
|
||||
"#define CONFIG_SPIRAM_USE_MALLOC 1\n")
|
||||
DEFINES = ["ESP32_PLATFORM=1", "BOARD_HAS_PSRAM=1", "STATION_G3_ESP32=1",
|
||||
"MESH_CLIENT_REPEATER_ONLY=1"]
|
||||
@@ -112,19 +111,19 @@ static std::vector<uint8_t> snapshot(const MyMesh& mesh) {
|
||||
const auto first = static_cast<const uint8_t*>(data);
|
||||
bytes.insert(bytes.end(), first, first + size);
|
||||
};
|
||||
add(mesh.flood_channel_scopes, sizeof(mesh.flood_channel_scopes));
|
||||
add(mesh.flood_channel_direct_scopes, sizeof(mesh.flood_channel_direct_scopes));
|
||||
add(mesh.flood_channel_scope_requirements, sizeof(mesh.flood_channel_scope_requirements));
|
||||
add(mesh.floodChannelScopes(), sizeof(mesh.floodChannelScopes()));
|
||||
add(mesh.floodChannelDirectScopes(), sizeof(mesh.floodChannelDirectScopes()));
|
||||
add(mesh.floodChannelScopeRequirements(), sizeof(mesh.floodChannelScopeRequirements()));
|
||||
return bytes;
|
||||
}
|
||||
static void populate(MyMesh& mesh) {
|
||||
for (unsigned i = 0; i < 32; ++i)
|
||||
std::snprintf(mesh.flood_channel_direct_scopes[i], 32, "#Scope%u", i);
|
||||
std::snprintf(mesh.floodChannelDirectScopes()[i], 32, "#Scope%u", i);
|
||||
for (unsigned i = 0; i < 255; ++i) {
|
||||
auto& row = mesh.flood_channel_scopes[i];
|
||||
auto& row = mesh.floodChannelScopes()[i];
|
||||
row.target_id = i % 32 + 1;
|
||||
row.selector = FloodFilterPolicy::encodeChannelScopeTargetSelector(0, true);
|
||||
auto& requirement = mesh.flood_channel_scope_requirements[i];
|
||||
auto& requirement = mesh.floodChannelScopeRequirements()[i];
|
||||
requirement.key_len = 32;
|
||||
std::memset(requirement.secret, uint8_t(i + 1), sizeof(requirement.secret));
|
||||
}
|
||||
@@ -132,10 +131,13 @@ static void populate(MyMesh& mesh) {
|
||||
static void exercise(const char* mode) {
|
||||
MemoryFS disk;
|
||||
{
|
||||
const unsigned constructor_calls = heap_test::heap().external_calls + heap_test::heap().internal_calls;
|
||||
MyMesh writer(disk);
|
||||
static_assert(sizeof(writer.flood_channel_scopes) == 255 * 36, "all scope slots");
|
||||
static_assert(sizeof(writer.flood_channel_direct_scopes) == 32 * 32, "all direct names");
|
||||
static_assert(sizeof(writer.flood_channel_scope_requirements) == 255 * 34, "all requirements");
|
||||
CHECK(heap_test::heap().external_calls + heap_test::heap().internal_calls == constructor_calls);
|
||||
writer.begin();
|
||||
static_assert(sizeof(writer.floodChannelScopes()) == 255 * 36, "all scope slots");
|
||||
static_assert(sizeof(writer.floodChannelDirectScopes()) == 32 * 32, "all direct names");
|
||||
static_assert(sizeof(writer.floodChannelScopeRequirements()) == 255 * 34, "all requirements");
|
||||
#if MESH_REPEATER_PSRAM_TABLES
|
||||
static_assert(sizeof(MyMesh) < 256, "no inline fallback arrays");
|
||||
auto& heap = heap_test::heap();
|
||||
@@ -153,24 +155,26 @@ static void exercise(const char* mode) {
|
||||
(void)mode;
|
||||
#endif
|
||||
CHECK(snapshot(writer) == std::vector<uint8_t>(18874, 0));
|
||||
const void* scopes = writer.flood_channel_scopes;
|
||||
const void* direct = writer.flood_channel_direct_scopes;
|
||||
const void* require = writer.flood_channel_scope_requirements;
|
||||
const void* scopes = writer.floodChannelScopes();
|
||||
const void* direct = writer.floodChannelDirectScopes();
|
||||
const void* require = writer.floodChannelScopeRequirements();
|
||||
populate(writer);
|
||||
const auto initial = snapshot(writer);
|
||||
writer.begin();
|
||||
CHECK(snapshot(writer) == initial);
|
||||
CHECK(writer.saveFloodChannelScopes());
|
||||
CHECK(writer.saveFloodChannelScopeRequirements());
|
||||
CHECK(disk.files["/scopes"].size() == 10210);
|
||||
CHECK(disk.files["/require"].size() == 8675);
|
||||
std::vector<uint8_t> expected = {'F','C','S','5',255};
|
||||
const auto* start = reinterpret_cast<const uint8_t*>(writer.flood_channel_scopes);
|
||||
const auto* start = reinterpret_cast<const uint8_t*>(writer.floodChannelScopes());
|
||||
expected.insert(expected.end(), start, start + 9180);
|
||||
expected.push_back(32);
|
||||
start = reinterpret_cast<const uint8_t*>(writer.flood_channel_direct_scopes);
|
||||
start = reinterpret_cast<const uint8_t*>(writer.floodChannelDirectScopes());
|
||||
expected.insert(expected.end(), start, start + 1024);
|
||||
CHECK(disk.files["/scopes"] == expected);
|
||||
expected = {'F','C','R','1',255};
|
||||
start = reinterpret_cast<const uint8_t*>(writer.flood_channel_scope_requirements);
|
||||
start = reinterpret_cast<const uint8_t*>(writer.floodChannelScopeRequirements());
|
||||
expected.insert(expected.end(), start, start + 8670);
|
||||
CHECK(disk.files["/require"] == expected);
|
||||
const auto saved = disk.files;
|
||||
@@ -191,9 +195,9 @@ static void exercise(const char* mode) {
|
||||
CHECK(snapshot(writer) == initial && disk.files == saved);
|
||||
}
|
||||
disk.fail_rename = disk.short_write = false;
|
||||
std::memset(writer.flood_channel_scopes, 0xa5, sizeof(writer.flood_channel_scopes));
|
||||
std::memset(writer.flood_channel_direct_scopes, 0xa5, sizeof(writer.flood_channel_direct_scopes));
|
||||
std::memset(writer.flood_channel_scope_requirements, 0xa5, sizeof(writer.flood_channel_scope_requirements));
|
||||
std::memset(writer.floodChannelScopes(), 0xa5, sizeof(writer.floodChannelScopes()));
|
||||
std::memset(writer.floodChannelDirectScopes(), 0xa5, sizeof(writer.floodChannelDirectScopes()));
|
||||
std::memset(writer.floodChannelScopeRequirements(), 0xa5, sizeof(writer.floodChannelScopeRequirements()));
|
||||
CHECK(writer.loadFloodChannelScopes());
|
||||
writer.loadFloodChannelScopeRequirements();
|
||||
CHECK(snapshot(writer) == initial);
|
||||
@@ -206,17 +210,17 @@ static void exercise(const char* mode) {
|
||||
CHECK(writer.loadFloodChannelScopes());
|
||||
writer.loadFloodChannelScopeRequirements();
|
||||
CHECK(snapshot(writer) == initial);
|
||||
CHECK(scopes == writer.flood_channel_scopes && direct == writer.flood_channel_direct_scopes
|
||||
&& require == writer.flood_channel_scope_requirements);
|
||||
CHECK(scopes == writer.floodChannelScopes() && direct == writer.floodChannelDirectScopes()
|
||||
&& require == writer.floodChannelScopeRequirements());
|
||||
// Compare the complete production file payloads between inline and PSRAM runs.
|
||||
for (const char* path : {"/scopes", "/require"}) {
|
||||
const auto& bytes = disk.files[path];
|
||||
CHECK(std::fwrite(bytes.data(), 1, bytes.size(), stdout) == bytes.size());
|
||||
}
|
||||
writer.deleteFloodChannelScope(".255", reply);
|
||||
CHECK(!std::strcmp(reply, "OK") && writer.flood_channel_scopes[254].target_id == 0);
|
||||
CHECK(!std::strcmp(reply, "OK") && writer.floodChannelScopes()[254].target_id == 0);
|
||||
writer.deleteFloodChannelScopeRequirement(".255", reply);
|
||||
CHECK(!std::strcmp(reply, "OK") && writer.flood_channel_scope_requirements[254].key_len == 0);
|
||||
CHECK(!std::strcmp(reply, "OK") && writer.floodChannelScopeRequirements()[254].key_len == 0);
|
||||
writer.deleteFloodChannelScope("all", reply);
|
||||
CHECK(!std::strcmp(reply, "OK"));
|
||||
writer.deleteFloodChannelScopeRequirement("all", reply);
|
||||
@@ -225,6 +229,8 @@ static void exercise(const char* mode) {
|
||||
}
|
||||
CHECK(heap_test::heap().blocks.empty());
|
||||
MyMesh recreated(disk);
|
||||
CHECK(heap_test::heap().blocks.empty());
|
||||
recreated.begin();
|
||||
CHECK(snapshot(recreated) == std::vector<uint8_t>(18874, 0));
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
@@ -260,10 +266,11 @@ def production_program():
|
||||
start = header.rfind("#if MESH_REPEATER_PSRAM_TABLES", 0, location)
|
||||
declarations = header[start:header.index("#if MESH_ENABLE_FLOOD_GROUP_MODERATION", location)]
|
||||
constructor = source.index("MyMesh::MyMesh(")
|
||||
start = source.index("#if MESH_REPEATER_PSRAM_TABLES", constructor)
|
||||
binders = source[start:source.index("#endif", start) + len("#endif")]
|
||||
start = source.index(" memset(flood_channel_scopes, 0, sizeof(flood_channel_scopes));", constructor)
|
||||
start = source.index("#if !MESH_REPEATER_PSRAM_TABLES\n memset(flood_channel_scopes", constructor)
|
||||
resets = source[start:source.index("#if MESH_ENABLE_FLOOD_GROUP_MODERATION", start)]
|
||||
begin_start = source.index("void MyMesh::begin(FILESYSTEM *fs) {")
|
||||
begin_start = source.index("{", begin_start) + 1
|
||||
begin = source[begin_start:source.index("#if MESH_ENABLE_FLOOD_RULE_ENGINE", begin_start)]
|
||||
methods = [
|
||||
"bool MyMesh::loadFloodChannelScopes(", "bool MyMesh::saveFloodChannelScopes(",
|
||||
"void MyMesh::loadFloodChannelScopeRequirements(",
|
||||
@@ -277,12 +284,13 @@ def production_program():
|
||||
"static bool isValidStoredFloodFilterScopeName(",
|
||||
"static bool isExactFloodChannelScopeSelector(",
|
||||
]
|
||||
owner = "class MyMesh {\npublic:\nMemoryFS* _fs;\n"
|
||||
owner = ("class MyMesh {\npublic:\nMemoryFS* _fs;\n"
|
||||
"struct Limiter { bool begin() { return true; } } flood_advert_limiter;\n")
|
||||
for name in ("FloodChannelScopeEntry", "FloodChannelScopeRequireEntry"):
|
||||
owner += body(header, "struct " + name + " {") + ";\n"
|
||||
owner += declarations + "\nint end_marker;\n"
|
||||
owner += "explicit MyMesh(MemoryFS& fs) : _fs(&fs),\n" + binders
|
||||
owner += "\nend_marker(0) {\n" + resets + "}\n"
|
||||
owner += declarations
|
||||
owner += "explicit MyMesh(MemoryFS& fs) : _fs(&fs) {\n" + resets + "}\n"
|
||||
owner += "void begin() {\n" + begin + "}\n"
|
||||
owner += ("bool loadFloodChannelScopes();\n"
|
||||
"bool saveFloodChannelScopes(bool empty_table=false);\n"
|
||||
"void loadFloodChannelScopeRequirements();\n"
|
||||
@@ -353,11 +361,17 @@ class RepeaterScopePsramTests(unittest.TestCase):
|
||||
cases = [([], 0, True, SDK), (["ESP32_PLATFORM=1", "STATION_G3_ESP32=1"], 0, True, SDK),
|
||||
(["ESP32_PLATFORM=1", "BOARD_HAS_PSRAM=1"], 0, True, SDK),
|
||||
(DEFINES[:-1], 0, True, SDK),
|
||||
([define for define in DEFINES if not define.startswith("STATION_G3")], 1, True, SDK),
|
||||
(DEFINES, 1, True, SDK), (DEFINES + ["MESH_REPEATER_PSRAM_TABLES=0"], 0, True, ""),
|
||||
(["MESH_REPEATER_PSRAM_TABLES=1"], 1, False, SDK),
|
||||
(DEFINES[:-1] + ["MESH_REPEATER_PSRAM_TABLES=1"], 1, False, SDK),
|
||||
(DEFINES[:-1] + ["MESH_CLIENT_REPEATER_ONLY=0", "MESH_REPEATER_PSRAM_TABLES=1"],
|
||||
1, False, SDK),
|
||||
(DEFINES + ["MESH_REPEATER_PSRAM_TABLES=2"], 2, False, SDK),
|
||||
(DEFINES, 1, False, SDK.replace("CONFIG_IDF_TARGET_ESP32S3 1", "CONFIG_IDF_TARGET_ESP32 1")),
|
||||
(DEFINES, 1, False, SDK.replace("CONFIG_SPIRAM_BOOT_INIT 1", "CONFIG_SPIRAM_BOOT_INIT 0")),
|
||||
(DEFINES, 0, True, SDK.replace("CONFIG_IDF_TARGET_ESP32S3 1", "CONFIG_IDF_TARGET_ESP32 1")),
|
||||
(DEFINES + ["MESH_REPEATER_PSRAM_TABLES=1"], 1, False,
|
||||
SDK.replace("CONFIG_IDF_TARGET_ESP32S3 1", "CONFIG_IDF_TARGET_ESP32 1")),
|
||||
(DEFINES, 1, True, SDK + "#define CONFIG_SPIRAM_BOOT_INIT 1\n"),
|
||||
(DEFINES, 1, False, SDK.replace("CONFIG_SPIRAM_USE_MALLOC 1", "CONFIG_SPIRAM_USE_MALLOC 0"))]
|
||||
for index, (defines, expected, success, config) in enumerate(cases):
|
||||
with self.subTest(index=index, defines=defines):
|
||||
|
||||
@@ -280,7 +280,9 @@ class WsTransportPsramTest(unittest.TestCase):
|
||||
cls.binaries = {}
|
||||
variants = {
|
||||
"g3_psram": (4, ["STATION_G3_ESP32", "BOARD_HAS_PSRAM"]),
|
||||
"other_psram": (4, ["BOARD_HAS_PSRAM"]),
|
||||
"generic_psram": (4, ["BOARD_HAS_PSRAM"]),
|
||||
"classic_psram": (4, ["CONFIG_IDF_TARGET_ESP32", "BOARD_HAS_PSRAM"]),
|
||||
"s3_psram": (4, ["CONFIG_IDF_TARGET_ESP32S3", "BOARD_HAS_PSRAM"]),
|
||||
"g3_internal": (4, ["STATION_G3_ESP32"]),
|
||||
"other_internal": (4, []),
|
||||
"idf5": (5, ["STATION_G3_ESP32", "BOARD_HAS_PSRAM"]),
|
||||
@@ -306,14 +308,15 @@ class WsTransportPsramTest(unittest.TestCase):
|
||||
capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
|
||||
def test_g3_psram_and_guarded_failure_lifecycles(self):
|
||||
for scenario in ["external_success", "internal_boundary", "reserve_denied",
|
||||
"largest_denied", "both_fail", "sdk_failure"]:
|
||||
with self.subTest(scenario=scenario):
|
||||
self.run_scenario("g3_psram", scenario)
|
||||
def test_board_agnostic_psram_and_guarded_failure_lifecycles(self):
|
||||
for variant in ["g3_psram", "generic_psram", "classic_psram", "s3_psram"]:
|
||||
for scenario in ["external_success", "internal_boundary", "reserve_denied",
|
||||
"largest_denied", "both_fail", "sdk_failure"]:
|
||||
with self.subTest(variant=variant, scenario=scenario):
|
||||
self.run_scenario(variant, scenario)
|
||||
|
||||
def test_other_target_allocation_defaults_and_failure_cleanup(self):
|
||||
for variant in ["other_psram", "g3_internal", "other_internal"]:
|
||||
for variant in ["g3_internal", "other_internal"]:
|
||||
for scenario in ["generic_success", "generic_failure", "sdk_failure"]:
|
||||
with self.subTest(variant=variant, scenario=scenario):
|
||||
self.run_scenario(variant, scenario)
|
||||
|
||||
Reference in new issue
Block a user