import tempfile import unittest from pathlib import Path from servos import ControleServos, PCA class Fake: def __init__(self): self.calls = [] def pulse(self, channel, value): self.calls.append((channel, value)) def off(self, channel): self.calls.append((channel, None)) class ServoTests(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.hw = Fake() self.c = ControleServos(Path(self.tmp.name) / 'servos.json', self.hw) def tearDown(self): self.c.close() self.tmp.cleanup() def test_open_does_not_move(self): self.assertEqual(len(self.c.status()['servos']), 6) self.assertEqual(self.hw.calls, []) def test_limits_and_types(self): for pulse in [1399, 1601, True, float('nan')]: with self.assertRaises(ValueError): self.c.move(0, pulse) self.assertEqual(self.hw.calls, []) def test_valid_pulse_and_stop(self): self.c.move(0, 1500) self.assertEqual(self.hw.calls[-1], (0, 1500)) self.c.stop() self.assertIn((0, None), self.hw.calls) def test_duplicate_channel_and_atomic_config(self): config = self.c.status()['servos'] config[1]['canal'] = 0 with self.assertRaises(ValueError): self.c.save(config) self.assertEqual(self.c.status()['servos'][1]['canal'], 1) def test_expiry_turns_off(self): self.c.move(1, 1500) self.c.expire(float('inf')) self.assertEqual(self.hw.calls[-1], (1, None)) def test_config_persists_and_supports_sixteen(self): rows = [dict(nome=f'Motor {i}',canal=i,minimo=1400,maximo=1600,centro=1500) for i in range(16)] self.c.save(rows) other = ControleServos(self.c.path, self.hw) try: self.assertEqual(other.status()['servos'], rows) finally: other.close() def test_frequency_initialization_disables_all_old_outputs(self): hw = PCA.__new__(PCA) hw.ready = False writes = [] hw.write = lambda reg, values: writes.append((reg, values)) hw.read = lambda reg: 0x11 hw.pulse(1, 1500) release = next(i for i, value in enumerate(writes) if value[0] == 10) for channel in range(16): self.assertIn((9 + 4*channel, [0x10]), writes[:release]) self.assertIn((0xFE, [121]), writes) self.assertIn((10, [0, 0, 51, 1]), writes) def test_global_register_cannot_overwrite_pulse_or_enable_other_channels(self): hw = PCA.__new__(PCA) hw.ready = False registers = {} def write(reg, values): for offset, value in enumerate(values): address = reg + offset registers[address] = value if 0xFA <= address <= 0xFD: for channel in range(16): registers[6 + 4*channel + address-0xFA] = value hw.write = write hw.read = lambda reg: 0x21 hw.pulse(1, 1500) self.assertEqual(registers[12] + ((registers[13] & 15) << 8), 307) for channel in range(16): if channel != 1: self.assertTrue(registers[9 + 4*channel] & 16) if __name__ == '__main__': unittest.main()