"""Source-session: identity, банковские условия и безопасные переменные.""" from pathlib import Path import sys import tempfile import unittest ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / 'toolchain')) from sdbg.image import read_ihx from sdbg.session import DebugSession, SessionError def record(kind, address, data=b''): body = bytes((len(data), address >> 8, address & 0xff, kind)) + data return ':' + (body + bytes((-sum(body) & 0xff,))).hex().upper() class FakeModel: def __init__(self, directory): self.directory = Path(directory) self.manifest = {'executable': 'test.exe', 'build_id': 'build'} self.stale_sources = [] self.symbols = {'_bank_pages': 0x8200} self.instructions = { 0x8100: {'link_address': 0x8100, 'logical_address': 0x8100, 'size': 2, 'bank': None, 'window': 2}, 0x1c000: {'link_address': 0x1c000, 'logical_address': 0xc000, 'size': 2, 'bank': 1, 'window': 3}, } self.functions = [ {'name': 'main', 'link_address': 0x8100, 'logical_address': 0x8100, 'start': 0x8100, 'end': 0x8102, 'bank': None, 'window': 2}, {'name': 'worker', 'link_address': 0x1c000, 'logical_address': 0xc000, 'start': 0x1c000, 'end': 0x1c002, 'bank': 1, 'window': 3}, ] self.variables = [ {'name': 'total', 'module': None, 'size': 2, 'type': 'SI:S', 'signed': True, 'supported': True, 'link_address': 0x8210, 'logical_address': 0x8210, 'bank': None, 'window': 2}, ] def verify_executable(self): return None def addr2line(self, address): return {'status': 'mapped', 'address': address} def line_locations(self, filename, line): return {'stale_source': bool(self.stale_sources), 'locations': [self.functions[1]] if line == 7 else []} class FakeBridge: def __init__(self, memory, pc=0x8100, pg3=5): self.memory = memory self.generation = 1 self.registers = {'PC': pc, 'PG0': 0, 'PG1': 1, 'PG2': 2, 'PG3': pg3} self.calls = [] self.next_id = 10 def handshake(self): return {'protocol': 1, 'capabilities': {'bank_guard': True}} def wait_stopped(self, timeout=5): return {'state': 'stopped', 'registers': dict(self.registers)} def request(self, command, **args): self.calls.append((command, args)) if command == 'memory': data = bytes(self.memory.get(args['address'] + i, 0) for i in range(args['length'])) return {'hex': data.hex()} if command == 'breakpoint': value = {'id': self.next_id} self.next_id += 1 return value if command == 'clear': return {'cleared': args['id']} raise AssertionError(command) class SessionTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() directory = Path(self.temp.name) ihx = '\n'.join(( record(0, 0x8100, b'\x21\x2a'), record(4, 0, b'\x00\x01'), record(0, 0xc000, b'\x3e\x07'), record(1, 0), )) + '\n' (directory / 'test.ihx').write_text(ihx) self.model = FakeModel(directory) self.memory = {0x8100: 0x21, 0x8101: 0x2a, 0xc000: 0x3e, 0xc001: 0x07, 0x8200: 0, 0x8201: 7, 0x8210: 0xfe, 0x8211: 0xff} def tearDown(self): self.temp.cleanup() def test_ihx_rejects_checksum(self): path = Path(self.temp.name) / 'bad.ihx' path.write_text(':00000001FE\n') with self.assertRaisesRegex(ValueError, 'сумм'): read_ihx(path) def test_attach_and_read_signed_variable(self): session = DebugSession(self.model, FakeBridge(self.memory)) attached = session.attach() self.assertEqual(attached['pc'], 0x8100) self.assertEqual(attached['bank_pages'], {1: 7}) self.assertEqual(session.read_variable('total')['value'], -2) def test_loaded_image_mismatch_is_rejected(self): self.memory[0x8100] = 0 with self.assertRaisesRegex(SessionError, 'не соответствует'): DebugSession(self.model, FakeBridge(self.memory)).attach() def test_banked_breakpoint_has_page_condition(self): bridge = FakeBridge(self.memory) session = DebugSession(self.model, bridge) session.attach() result = session.break_line('worker.c', 7) self.assertEqual(result['backend_ids'], [10]) self.assertIn(('breakpoint', {'address': 0xc000, 'window': 3, 'page': 7}), bridge.calls) session.clear_breakpoint(result['id']) def test_stale_source_and_unmapped_bank_are_rejected(self): session = DebugSession(self.model, FakeBridge(self.memory)) session.attach() self.model.stale_sources = ['worker.c'] with self.assertRaisesRegex(SessionError, 'пересоберите'): session.break_line('worker.c', 7) bridge = FakeBridge(self.memory, pc=0xc000, pg3=7) session = DebugSession(self.model, bridge) attached = session.attach() self.assertEqual(attached['location']['link_address'], 0x1c000) if __name__ == '__main__': unittest.main()