Files
MeshCore/test/test_nrf52_flash_trim.py
mikecarper 8c698dc2d1 Reduce nRF52 Companion flash and stream Reader chapters
Use a 960-byte constant-time Ed25519 base table, compact hash/scalar code and unused-library trims while retaining Reader, BLE P-256 and the 8 KiB stack. RAK4631 Full saves 57,496 flash bytes and 3,152 static RAM bytes versus preview3.

Support complete chapters and verse ranges with bounded app continuation replies and cooperative terminal output. Use nonblocking TCP writes under reader backpressure. Add scalar/signature comparisons and a linked ARM crypto/stack CI gate.

Validated clean RAK4631, T1000-E, T096 and V4 Full builds, host regressions, and physical RAK chapter/reconnect and signing comparisons. Physical signing averages 35.9 ms versus 14.4 ms with the original table.
2026-10-13 01:02:30 -07:00

222 lines
12 KiB
Python

import importlib.util
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location("nrf52_flash_trim", ROOT / "scripts/nrf52_flash_trim.py")
TRIM = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(TRIM)
class Nrf52FlashTrimTest(unittest.TestCase):
def policy(self, name="RAK_4631_repeater", role="simple_repeater", **kwargs):
options = dict(platform="nordicnrf52", mcu="nrf52840", env_name=name,
src_filter="+<*.cpp> +<../examples/%s>" % role)
options.update(kwargs)
return TRIM.trim_policy(**options)
def test_every_infrastructure_role_keeps_compact_crypto_and_names(self):
for role in ("simple_repeater", "simple_room_server", "simple_sensor", "kiss_modem"):
with self.subTest(role=role):
defines = self.policy(role=role)
for name in ("MESH_NRF52_FLASH_TRIM", "ED25519_COMPACT_BASE",
"ED25519_COMPACT_SHA512", "OTA_TARGET_NAME_FRONT_CODED",
"SSD1306_NO_SPLASH"):
self.assertEqual(defines[name], 1)
self.assertEqual(defines["CFG_TUD_HID"], 0)
self.assertNotIn("CFG_TUD_CDC", defines)
self.assertFalse(any(name.startswith("CFG_TUH") for name in defines))
def test_every_companion_alias_is_excluded(self):
for suffix in ("companion_radio_full", "companion_radio_usb", "companion_radio_ble",
"companion_radio_wifi", "companion_radio_serial", "companion_radio_ethernet",
"companion_usb", "companion_ble", "comp_radio_usb", "terminal_chat", "term_chat"):
with self.subTest(suffix=suffix):
self.assertEqual(self.policy(name="RAK_4631_" + suffix), {})
def test_source_filter_and_synthetic_alias_also_exclude_companion(self):
for role in ("companion_radio/*.cpp", "simple_secure_chat/main.cpp"):
self.assertEqual(self.policy(name="custom", role=role), {})
for flags in ('-DCOMPANION_RADIO_FULL=1', '-D COMPANION_FEATURE_USB_MOTA_SOURCE=1',
'-DOTA_VARIANT=\'"RAK_4631_terminal_chat"\'',
'-DOTA_VARIANT=\'"RAK_4631_companion_radio_full"\''):
with self.subTest(flags=flags):
self.assertEqual(self.policy(flags=flags), {})
self.assertEqual(self.policy(cppdefines=[("COMPANION_RADIO_FULL", 1)]), {})
def test_unknown_roles_other_platforms_and_explicit_optout_are_unchanged(self):
for options in (dict(role="custom"), dict(platform="espressif32", mcu="esp32"),
dict(platform="ststm32", mcu="stm32f411"),
dict(flags="-DMESH_NRF52_FLASH_TRIM=0")):
self.assertEqual(self.policy(**options), {})
def test_effective_filter_is_ordered_and_does_not_include_outside_source_by_wildcard(self):
self.assertEqual(self.policy(src_filter="+<*> +<../examples/simple_repeater> -<../examples/simple_repeater>"), {})
self.assertEqual(self.policy(src_filter="+<*>"), {})
self.assertTrue(self.policy(src_filter="+<../examples/companion_radio> -<../examples/companion_radio> +<../examples/simple_repeater>"))
def test_board_and_user_defines_are_not_overridden(self):
defines = self.policy(flags="-D CFG_TUD_HID=1 -UED25519_COMPACT_BASE -USSD1306_NO_SPLASH")
self.assertNotIn("CFG_TUD_HID", defines)
self.assertNotIn("ED25519_COMPACT_BASE", defines)
self.assertNotIn("SSD1306_NO_SPLASH", defines)
self.assertEqual(TRIM.flag_defines("-DMESH_NRF52_FLASH_TRIM=1 -UMESH_NRF52_FLASH_TRIM")["MESH_NRF52_FLASH_TRIM"], None)
def test_cc310_archive_is_exact_and_requires_supported_mcu_and_enabled_crypto(self):
with tempfile.TemporaryDirectory() as temp_dir:
archive = Path(temp_dir) / "libraries/Adafruit_nRFCrypto" / TRIM.CC310_ARCHIVE
archive.parent.mkdir(parents=True)
archive.write_bytes(b"future-sdk")
enabled = {"USE_CC310_HW_CRYPTO": "1"}
self.assertFalse(TRIM.supported_cc310(temp_dir, "nrf52840", enabled))
with patch.object(TRIM, "CC310_ARCHIVE_SHA256", TRIM.hashlib.sha256(b"future-sdk").hexdigest()):
self.assertTrue(TRIM.supported_cc310(temp_dir, "nrf52840", enabled))
self.assertFalse(TRIM.supported_cc310(temp_dir, "nrf52832", enabled))
self.assertFalse(TRIM.supported_cc310(temp_dir, "nrf52840", {"USE_CC310_HW_CRYPTO": "0"}))
self.assertFalse(TRIM.supported_cc310(temp_dir, "nrf52840", {}))
def test_profile_wiring_is_nrf52_only_and_companion_common_flags_unchanged(self):
config = (ROOT / "platformio.ini").read_text()
nrf = config.split("[nrf52_base]", 1)[1].split("\n[", 1)[0]
self.assertIn("pre:scripts/nrf52_flash_trim.py", nrf)
self.assertEqual(config.count("pre:scripts/nrf52_flash_trim.py"), 1)
self.assertNotIn("ED25519_COMPACT", nrf)
self.assertNotIn("CFG_TUD_HID", nrf)
self.assertNotIn("SSD1306_NO_SPLASH", nrf)
self.assertIn("-D MESH_NRF52_LOOP_STACK_WORDS=2048", nrf)
def test_tft_force_include_is_per_library_and_preserves_existing_include_pair(self):
self.assertTrue(TRIM.source_selected("+<helpers/ui/ST7735Display.cpp>",
"helpers/ui/ST7735Display.cpp"))
self.assertFalse(TRIM.source_selected("+<helpers/ui/*.cpp> -<helpers/ui/ST7735Display.cpp>",
"helpers/ui/ST7735Display.cpp"))
class LibraryEnvironment(dict):
def Append(self, **kwargs):
for key, value in kwargs.items():
self.setdefault(key, []).extend(value)
def subst(self, text):
return text.replace("$PROJECT_DIR", str(ROOT))
library = LibraryEnvironment(CCFLAGS=["-include", "SoftDeviceSvcCompat.h"])
source = object()
self.assertIs(TRIM.trim_tft_library(library, source), source)
self.assertEqual(library["CCFLAGS"].count("-include"), 2)
before = list(library["CCFLAGS"])
TRIM.trim_tft_library(library, source)
self.assertEqual(library["CCFLAGS"], before)
def test_install_does_not_mutate_excluded_roles_and_scopes_wrapper_build(self):
class Platform:
name = "nordicnrf52"
def get_package_dir(self, _name):
return "sdk"
class BuildEnvironment(dict):
def PioPlatform(self):
return Platform()
def BoardConfig(self):
return {"build.mcu": "nrf52840"}
def AppendUnique(self, **kwargs):
for name, values in kwargs.items():
for value in values:
if value not in self.setdefault(name, []):
self[name].append(value)
def BuildSources(self, *args):
self.setdefault("BUILT_SOURCES", []).append(args)
base = dict(BUILD_FLAGS=["-DUSE_CC310_HW_CRYPTO=1"],
SRC_FILTER=["+<../examples/simple_repeater>"], CPPDEFINES=[])
excluded = BuildEnvironment(**base, PIOENV="RAK_4631_companion_radio_full")
before = dict(excluded)
TRIM.install(excluded)
self.assertEqual(excluded, before)
active = BuildEnvironment(BUILD_FLAGS=list(base["BUILD_FLAGS"]),
SRC_FILTER=list(base["SRC_FILTER"]), CPPDEFINES=[],
PIOENV="RAK_4631_repeater")
with patch.object(TRIM, "supported_cc310", return_value=True):
TRIM.install(active)
self.assertTrue(active["MESH_NRF52_FLASH_TRIM_ACTIVE"])
self.assertIn("-Wl,--wrap=CRYS_ECPKI_GetEcDomain", active["LINKFLAGS"])
self.assertEqual(active["BUILT_SOURCES"][0][-1], "+<Cc310DomainTrim.c>")
@unittest.skipUnless(shutil.which("cc"), "a native C compiler is required")
def test_wrapped_domain_preserves_p256_and_rejects_unused_curves(self):
fixture = ROOT / "test/fixtures/nrf52_flash_trim"
with tempfile.TemporaryDirectory() as temp_dir:
executable = Path(temp_dir) / "cc310-trim-test"
subprocess.run(["cc", "-std=c99", "-Wall", "-Wextra", "-Werror",
"-DMESH_NRF52_FLASH_TRIM=1", "-I" + str(fixture),
str(ROOT / "src/helpers/nrf52/Cc310DomainTrim.c"),
str(fixture / "test_domain_trim.c"), "-o", str(executable)], check=True)
subprocess.run([str(executable)], check=True)
def test_companion_keeps_small_crypto_stack_fonts_reader_and_usb_transports(self):
for platform, mcu, expected in (("nordicnrf52", "nrf52840", True),
("nordicnrf52", "nrf52832", True),
("espressif32", "esp32s3", False)):
with self.subTest(platform=platform, mcu=mcu):
defines = TRIM.companion_trim_policy(platform, mcu,
"+<*> +<../examples/companion_radio/*.cpp>")
self.assertEqual(bool(defines), expected)
if not expected:
continue
self.assertEqual(defines["MESH_NRF52_COMPANION_TRIM"], 1)
self.assertEqual(defines["ED25519_SMALL_BASE"], 1)
self.assertEqual(defines["ED25519_COMPACT_SHA512"], 1)
self.assertEqual(defines["ED25519_COMPACT_SCALAR"], 1)
self.assertEqual(defines["CFG_TUD_MSC"], 0)
self.assertEqual(defines["CFG_TUD_HID"], 0)
for name in ("MESH_NRF52_FLASH_TRIM", "ED25519_COMPACT_BASE",
"CFG_TUD_CDC", "CFG_TUD_DFU",
"CFG_TUD_DFU_RUNTIME", "COMPANION_FEATURE_READER"):
self.assertNotIn(name, defines)
self.assertFalse(any(name.startswith("CFG_TUH") for name in defines))
def test_companion_role_requires_selected_source_and_honors_explicit_options(self):
for sources in ("+<*>", "+<../examples/simple_secure_chat>",
"+<../examples/companion_radio> -<../examples/companion_radio>",
"+<../examples/companion_radio> +<../examples/simple_repeater>"):
self.assertEqual(TRIM.companion_trim_policy("nordicnrf52", "nrf52840", sources), {})
for flags in ("-DMESH_NRF52_COMPANION_TRIM=0", "-UMESH_NRF52_COMPANION_TRIM",
"-DMESH_NRF52_FLASH_TRIM=0"):
self.assertEqual(TRIM.companion_trim_policy("nordicnrf52", "nrf52840",
"+<../examples/companion_radio>", flags), {})
result = TRIM.companion_trim_policy("nordicnrf52", "nrf52840",
"+<../examples/companion_radio>", "-DCFG_TUD_MSC=1 -USSD1306_NO_SPLASH")
self.assertNotIn("CFG_TUD_MSC", result)
self.assertNotIn("SSD1306_NO_SPLASH", result)
result = TRIM.companion_trim_policy("nordicnrf52", "nrf52840",
"+<../examples/companion_radio>",
"-DED25519_SMALL_BASE=0 -UED25519_COMPACT_SHA512 -UED25519_COMPACT_SCALAR")
for name in ("ED25519_SMALL_BASE", "ED25519_COMPACT_SHA512", "ED25519_COMPACT_SCALAR"):
self.assertNotIn(name, result)
for value in (0, 1):
result = TRIM.companion_trim_policy("nordicnrf52", "nrf52840",
"+<../examples/companion_radio>", "-DED25519_COMPACT_BASE=%d" % value)
self.assertNotIn("ED25519_SMALL_BASE", result)
@unittest.skipUnless(shutil.which("cc"), "a native C compiler is required")
def test_companion_uses_same_p256_domain_wrapper(self):
fixture = ROOT / "test/fixtures/nrf52_flash_trim"
with tempfile.TemporaryDirectory() as temp_dir:
executable = Path(temp_dir) / "cc310-companion-test"
subprocess.run(["cc", "-std=c99", "-Wall", "-Wextra", "-Werror",
"-DMESH_NRF52_COMPANION_TRIM=1", "-I" + str(fixture),
str(ROOT / "src/helpers/nrf52/Cc310DomainTrim.c"),
str(fixture / "test_domain_trim.c"), "-o", str(executable)], check=True)
subprocess.run([str(executable)], check=True)
if __name__ == "__main__":
unittest.main()