import importlib.util import json import tempfile import unittest from pathlib import Path from test_bridge import bridge source=Path(__file__).resolve().parents[2]/'tools/pi_gpio/device_feedback.py' spec=importlib.util.spec_from_file_location('feedback',source) feedback=importlib.util.module_from_spec(spec);spec.loader.exec_module(feedback) class Pin: def __init__(self,pin,**kwargs): self.value=False;self.closed=False def on(self):self.value=True def off(self):self.value=False def close(self):self.closed=True class FeedbackTests(unittest.TestCase): def config(self,data,pins): with tempfile.TemporaryDirectory() as folder: path=Path(folder)/'devices.json';path.write_text(json.dumps(data)) return feedback.load_devices(path,pins) def test_pin_ownership(self): with self.assertRaises(ValueError):self.config([dict(id='r',port='J1',kind='rfid')],[17]) with self.assertRaises(ValueError):self.config([dict(id='b',port='J17',kind='input',pull_up=True)],[16]) with self.assertRaises(ValueError):self.config([dict(id='b',port='J17',kind='input')],[25]) with self.assertRaises(ValueError):self.config([dict(id='x',port='J22',kind='output')],[18]) result=self.config([dict(id='r',port='J2',kind='rfid'),dict(id='b',port='J17',kind='input',pull_up=True)],[25]) self.assertEqual(result[1]['pin'],16) def test_live_values_transitions_age_and_cleanup(self): now=[100] definitions=self.config([dict(id='button',port='J17',kind='input',pull_up=True),dict(id='lamp',port='J22',kind='output')],[25]) outputs=bridge.Outputs(Pin,'test',[25],clock=lambda:now[0]) monitor=feedback.Feedback(definitions,Pin,outputs,clock=lambda:now[0]) monitor.tick();first=monitor.snapshot();monitor.tick() self.assertEqual(len(first['events']),len(monitor.snapshot()['events'])) monitor.inputs[0][1].value=True outputs.command(dict(controller='test',pin=25,mode='pulse',duration_ms=200,active_high=True)) monitor.tick() self.assertTrue(all(d['value'] for d in monitor.snapshot()['devices'])) now[0]+=.3;outputs.tick();monitor.tick() self.assertFalse(monitor.snapshot()['devices'][1]['value']) now[0]+=7 self.assertGreater(monitor.snapshot()['devices'][0]['age_ms'],6000) self.assertGreater(monitor.snapshot()['events'][0]['age_ms'],6000) self.assertNotIn('stamp',monitor.snapshot()['events'][0]) pin=monitor.inputs[0][1];monitor.close();outputs.close();self.assertTrue(pin.closed) def test_rfid_requires_helper(self): outputs=bridge.Outputs(Pin,'test',[25]) with self.assertRaises(ValueError):feedback.Feedback([dict(id='r',kind='rfid',port='J1')],Pin,outputs) outputs.close()