AnasSarkiz/magnetic-shutter-remote-r8

A USB‑C rechargeable battery-powered ESP32‑C3 wireless controller with Li‑ion charging, protected 3.3‑V buck‑boost regulation, thermal/safety monitoring, status LEDs, buttons, programming UART, crystal clock, and 2.4‑GHz antenna matching network.

Version
0.4.0-prototype
License
unset
Stars
0

tests/test_standard_jst_programmer.py

"""Guard the real UART contract and the manual-reset, DTR-enabled host helper."""
import hashlib
import io
from contextlib import redirect_stdout
import importlib.util
import json
import tempfile
import unittest
from pathlib import Path
from types import SimpleNamespace

ROOT=Path(__file__).resolve().parents[1]
SPEC=importlib.util.spec_from_file_location('r8_uart_flash',ROOT/'scripts/flash-r8-standard-jst.py')
FLASH=importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(FLASH)
EVIDENCE=ROOT/'evidence/R8-standard-programmer-2026-10-05/qualification'


class StandardProgrammer(unittest.TestCase):
    def test_published_host_and_target_contacts(self):
        c=json.loads((EVIDENCE/'programmer-0.8.0/circuit.json').read_text())
        j5=next(x for x in c if x['type']=='source_component' and x['name']=='J5')
        ports={p['pin_number']:p['name'] for p in c if p['type']=='source_port' and p['source_component_id']==j5['source_component_id']}
        self.assertEqual(ports,{1:'TX',2:'GND',3:'RX'})
        target=(ROOT/'src/remote-circuit.tsx').read_text().split('<BM03B_SRSS_TB_LF__SN_')[1].split('/>')[0]
        for pin,net in [(1,'UART_RX'),(2,'GND'),(3,'UART_TX'),(4,'GND'),(5,'GND')]:
            self.assertIn(f'pin{pin}: "net.{net}"',target)
        self.assertNotIn('net.V3',target)
        self.assertNotIn('net.EN',target)
        self.assertNotIn('net.BOOT',target)

    def test_default_image_is_preserved_c3(self):
        raw=FLASH.DEFAULT_IMAGE.read_bytes()
        self.assertEqual(hashlib.sha256(raw).hexdigest(),FLASH.DEFAULT_SHA256)
        self.assertEqual(raw[0],0xe9)
        self.assertEqual(int.from_bytes(raw[12:14],'little'),5)

    def test_image_validation_rejects_changed_wrong_chip_and_checksum(self):
        class Image:
            chip_id=5;checksum=1;append_digest=False
            def __init__(self,stream): self.data=stream.read()
            def verify(self): pass
            def calculate_checksum(self): return 1
        with tempfile.TemporaryDirectory() as directory:
            p=Path(directory)/'test.bin';p.write_bytes(b'valid')
            sha=hashlib.sha256(p.read_bytes()).hexdigest()
            self.assertEqual(FLASH.validate_image(p,sha,Image,5)['bytes'],5)
            with self.assertRaisesRegex(ValueError,'size/hash'):
                FLASH.validate_image(p,'0'*64,Image,5)
            with self.assertRaisesRegex(ValueError,'Only ESP32-C3'):
                FLASH.validate_image(p,sha,Image,12)
            Image.checksum=0
            with self.assertRaisesRegex(ValueError,'checksum'):
                FLASH.validate_image(p,sha,Image,5)

    def test_serial_open_preserves_dtr_and_manual_reset(self):
        events=[]
        class Port:
            def __init__(self,**kwargs):
                self.is_open=False
                self.port=kwargs['port']
                self.baudrate=kwargs['baudrate']
                self.dtr=False;self.rts=True
            def __enter__(self): return self
            def __exit__(self,*args): events.append('closed')
            def open(self):
                events.append(('open',self.port,self.dtr,self.rts,self.baudrate))
                self.is_open=True
        class Rom:
            secure_download_mode=False
            CHIP_DETECT_MAGIC_REG_ADDR=0x40001000
            CHIP_DETECT_MAGIC_VALUE=[0x6921506f]
            def read_reg(self,address): return 0x6921506f
            def __init__(self,port,baud): self.port=port
            def connect(self,mode): events.append(('connect',mode))
        def main(argv,esp): events.append(('esptool',argv,esp.port.dtr))
        with redirect_stdout(io.StringIO()):
            FLASH.flash('CDC0',Path('reviewed.bin'),SimpleNamespace(Serial=Port),SimpleNamespace(main=main),Rom)
        self.assertEqual(events[0],('open','CDC0',True,False,115200))
        self.assertEqual(events[1],('connect','no_reset'))
        commands=[e for e in events if isinstance(e,tuple) and e[0]=='esptool']
        self.assertEqual(len(commands),2)
        for _,argv,dtr in commands:
            self.assertTrue(dtr)
            self.assertEqual(argv[argv.index('--before')+1],'no_reset')
            self.assertEqual(argv[argv.index('--after')+1],'no_reset')
            self.assertIn('--no-stub',argv)
            self.assertIn('0x0',argv)
        self.assertIn('write_flash',commands[0][1]);self.assertIn('verify_flash',commands[1][1])