diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index 7ef970b9c..79aed4f28 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -96,6 +96,7 @@ jobs: - name: Verify compact STM32 signature arithmetic run: | + python3 -B test/test_stm32_companion_profile.py -v python3 -B test/test_ed25519_compact_scalar.py -v python3 -B test/test_ed25519_compact.py -v diff --git a/test/test_stm32_companion_profile.py b/test/test_stm32_companion_profile.py new file mode 100644 index 000000000..aa82cd8fb --- /dev/null +++ b/test/test_stm32_companion_profile.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Guard the headless RAK Companion's size recipe and deployed storage contract. + +The stm32-companion-smoke job also links the actual image. These checks prevent +fixing its size limit by silently shrinking storage or Companion capacities. +""" + +import configparser +from pathlib import Path +import re +import unittest + + +ROOT = Path(__file__).resolve().parents[1] + + +class STM32CompanionProfileTest(unittest.TestCase): + def setUp(self): + self.config = configparser.ConfigParser( + interpolation=None, inline_comment_prefixes=(";",)) + self.config.read(ROOT / "variants/rak3x72/platformio.ini") + self.companion = self.config["env:RAK_3x72_companion_radio_usb"] + + def test_unused_timer_wrappers_are_removed_only_from_headless_companion(self): + for flag in ("RADIOLIB_TONE_UNSUPPORTED", "HAL_TIM_MODULE_ONLY"): + self.assertRegex(self.companion["build_flags"], r"-D\s+" + flag + r"\b") + for section in self.config.sections(): + if section != self.companion.name: + self.assertNotIn(flag, self.config[section].get("build_flags", "")) + + def test_flash_and_filesystem_boundaries_do_not_move(self): + self.assertNotIn("board_upload.maximum_size", self.companion) + self.assertEqual(self.config["rak3x72"].getint("board_upload.maximum_size"), + 229376) + self.assertNotIn("LFS_FLASH_TOTAL_SIZE", self.companion["build_flags"]) + self.assertNotIn("LFS_FLASH_TOTAL_SIZE", self.config["rak3x72"]["build_flags"]) + header = (ROOT / "src/helpers/stm32/InternalFileSystem.h").read_text() + self.assertRegex(header, r"#define\s+LFS_FLASH_TOTAL_SIZE\s+\(16\s*\*\s*2048\)") + self.assertEqual(229376 + 16 * 2048, 256 * 1024) + + def test_companion_capacities_and_interfaces_are_retained(self): + flags = self.companion["build_flags"] + for definition in ("MAX_CONTACTS=100", "MAX_GROUP_CHANNELS=8", + "ENABLE_USB_INTERFACE"): + self.assertRegex(flags, r"-D\s+" + re.escape(definition) + r"\b") + for flags in (flags, self.config["rak3x72"]["build_flags"]): + self.assertNotRegex(flags, r"COMPANION_FEATURE_\w+=0") + self.assertNotRegex(flags, r"MESH_ENABLE_\w+=0") + self.assertIn("+<../examples/companion_radio/*.cpp>", + self.companion["build_src_filter"]) + self.assertIn("-Oz", self.companion["build_flags"]) + self.assertEqual(self.companion["build_unflags"], "-Os") + + +if __name__ == "__main__": + unittest.main() diff --git a/variants/rak3x72/platformio.ini b/variants/rak3x72/platformio.ini index ae47ba55a..ddd87e295 100644 --- a/variants/rak3x72/platformio.ini +++ b/variants/rak3x72/platformio.ini @@ -66,6 +66,10 @@ extends = rak3x72 platform_packages = platformio/toolchain-gccarmnoneeabi@^1.140201.0 build_unflags = -Os build_flags = ${rak3x72.build_flags} + ; This headless UART Companion uses GPIO, ADC and SysTick, not PWM/tone. + ; Drop unused Arduino timer wrappers, retaining the low-level STM32 TIM HAL. + -D RADIOLIB_TONE_UNSUPPORTED + -D HAL_TIM_MODULE_ONLY -D ED25519_COMPACT_SHA512=1 ; same hash rounds, compact loop preserves the filesystem boundary -D ED25519_COMPACT_SCALAR=1 ; same ref10 arithmetic with fixed loops instead of unrolled code -Oz