Files
HaloKeymind/test/test_nrf52_bootloader_version.py
T

84 lines
4.8 KiB
Python

"""Compile the real version reader; optionally verify the pinned 2.4.6 release."""
import hashlib
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest
import zipfile
ROOT = Path(__file__).resolve().parents[1]
class BootloaderVersionTest(unittest.TestCase):
def test_firmware_uses_shared_reader(self):
board = (ROOT / "src/helpers/NRF52Board.cpp").read_text()
getter = board.split("bool NRF52Board::getBootloaderVersion(", 1)[1].split("\n}", 1)[0]
self.assertIn("mesh::nrf52BootloaderRegion(", getter)
self.assertIn("NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE", getter)
self.assertIn("mbr_start, NRF_UICR->NRFFW[0]", getter)
self.assertIn("mesh::nrf52BootloaderVersion(", getter)
self.assertIn("bootloaderVersion, out, max_len", getter)
self.assertNotIn("0x000FB000", getter)
common = (ROOT / "src/helpers/CommonCLI.cpp").read_text()
cli = common.split('configKeyEquals(config, "bootloader.ver")', 1)[1].split("} else {", 1)[0]
self.assertIn("char ver[128]", cli)
self.assertIn("_board->getBootloaderVersion(ver, sizeof(ver))", cli)
def test_native_version_reader(self):
compiler = shutil.which(os.environ.get("CXX", "g++"))
self.assertIsNotNone(compiler, "g++ (or CXX) is required for this native test")
with tempfile.TemporaryDirectory(prefix="meshcore-boot-version-") as temp:
exe = Path(temp) / ("test.exe" if os.name == "nt" else "test")
command = [compiler, "-std=c++17", "-O2", "-Wall", "-Wextra", "-Werror", "-Wno-unused-parameter",
"-I" + str(ROOT / "src"), "-isystem", str(ROOT / "test/mocks"),
str(ROOT / "test/fixtures/nrf52_bootloader_version/test_version.cpp"),
"-o", str(exe)]
built = subprocess.run(command, capture_output=True, text=True, timeout=60)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
result = subprocess.run([str(exe)], capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn("native cases passed", result.stdout)
# Optional local/release artifacts; never downloaded or flashed by
# this test. Each must be a raw bootloader image starting at F4000.
for path in os.environ.get("MESHCORE_BOOTLOADER_IMAGES", "").split(os.pathsep):
if path:
with self.subTest(image=path):
result = subprocess.run([str(exe), path], check=True, capture_output=True,
text=True, timeout=20)
self.assertRegex(result.stdout, r"\d+\.\d+\.\d+")
release = os.environ.get("MESHCORE_BOOTLOADER_MOTA_ZIP")
if release:
self.assertEqual(hashlib.sha256(Path(release).read_bytes()).hexdigest(),
"04fe6d1b11b0c3b926289d6384dae5cb878462da"
"a7fbbe9afb70f42d869f8003", "unexpected 2.4.6 release archive")
sys.path.insert(0, str(ROOT / "tools/mota"))
import motalib
with zipfile.ZipFile(release) as archive:
names = [name for name in archive.namelist() if name.endswith(".mota")]
self.assertEqual(len(names), 17)
sd_name = "mota/update-heltec_mesh_tower_v2_sdcard_bootloader-0.11.0-OTAFIX2.4.6.mota"
self.assertIn(sd_name, names)
for name in names:
with self.subTest(release_image=name):
parsed = motalib.parse_container(archive.read(name))
self.assertEqual(motalib.verify(parsed), [])
if name == sd_name:
self.assertNotIn(b"UF2 Bootloader ", parsed.payload)
identity = motalib.parse_bootloader_identity(parsed.payload)
self.assertEqual(identity.crc32, 0x5DACDB3D)
self.assertEqual(identity.boot_version, 0x020406FF)
result = subprocess.run([str(exe), "--hex"], input=parsed.payload.hex(),
capture_output=True, text=True, timeout=20)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn("OTAFIX2.4.6", result.stdout)
if name == sd_name:
self.assertEqual(result.stdout.strip(), "OTAFIX2.4.6")
print(f"Verified version reader against {len(names)} signed OTAFIX 2.4.6 release images")
if __name__ == "__main__":
unittest.main()