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/cam/test_board_boundaries.py

"""Reject stale outlines and incomplete RF exclusions after PCB resizing."""
import copy
import importlib.util
import json
from pathlib import Path
import sys
import unittest
from shapely.geometry import box

ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT/'scripts'))
from r8_board_geometry import board_outline, rf_exclusion
from r9_board_geometry import antenna_exclusion
from curved_geometry import brep_polygon

SPEC = importlib.util.spec_from_file_location('boundary_readback',ROOT/'scripts/review-r3-exports.py')
READER = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = READER
SPEC.loader.exec_module(READER)


class BoardBoundariesTest(unittest.TestCase):
    def setUp(self):
        self.circuit = json.loads((ROOT/'dist/index/circuit.json').read_text())

    def test_full_outline_rejects_removed_corner_rounding_with_identical_bounds(self):
        outline = board_outline(self.circuit)
        with self.assertRaises(ValueError):
            READER.compare_geometry(READER.GeometryComparison(outline,box(*outline.bounds),'outline'))
        READER.compare_geometry(READER.GeometryComparison(outline,outline,'outline'))

    def test_dimension_change_cannot_reuse_stale_native_outline(self):
        board = next(e for e in self.circuit if e['type']=='pcb_board')
        board['width'] -= 4
        with self.assertRaises(ValueError):
            board_outline(self.circuit)

    def test_reduced_or_single_layer_antenna_band_is_rejected(self):
        # Retain the module-era regression against its actual retained R8 circuit.
        reference = json.loads((ROOT/'evidence/R8-compact-pcb-2026-10-06/route21-schematic-review/circuit.json').read_text())
        rf_exclusion(reference)
        source = next(e for e in reference if e['type']=='source_component' and e['name']=='U1')
        radio = next(e for e in reference if e['type']=='pcb_component'
                     and e['source_component_id']==source['source_component_id'])
        band = next(e for e in reference if e['type']=='pcb_keepout'
                    and e.get('excluded_pcb_component_ids')==[radio['pcb_component_id']])
        for broken in ['width','layers']:
            circuit = copy.deepcopy(reference)
            changed = next(e for e in circuit if e.get('pcb_keepout_id')==band['pcb_keepout_id'])
            if broken=='width':changed['width'] -= 2
            else:changed['layers']=['top']
            with self.assertRaises(ValueError):
                rf_exclusion(circuit)

    def test_bare_chip_antenna_exclusion_rejects_reduced_or_single_layer_band(self):
        antenna, _ = antenna_exclusion(self.circuit)
        band = next(e for e in self.circuit if e['type']=='pcb_keepout'
                    and e.get('excluded_pcb_component_ids')==[antenna['pcb_component_id']])
        for broken in ['width','layers']:
            circuit = copy.deepcopy(self.circuit)
            changed = next(e for e in circuit if e.get('pcb_keepout_id')==band['pcb_keepout_id'])
            if broken=='width':changed['width'] -= 2
            else:changed['layers']=['top']
            with self.assertRaises(ValueError):
                antenna_exclusion(circuit)

    def test_original_tangent_cutout_ring_must_not_be_silently_repaired(self):
        circuit = json.loads((ROOT/'evidence/R8-compact-pcb-2026-10-06/failed-route10/circuit.json').read_text())
        pour = next(e for e in circuit if e.get('pcb_copper_pour_id')=='pcb_copper_pour_24')
        with self.assertRaisesRegex(ValueError, 'Invalid BREP polygon'):
            brep_polygon(pour['brep_shape'])