Add shared-session MCP source debugger adapter

This commit is contained in:
Александр Петров
2026-09-16 20:43:50 +03:00
parent addc00a0f3
commit 9ad5a018a2
19 changed files with 740 additions and 22 deletions
+43 -1
View File
@@ -4,6 +4,7 @@ import sys
import tempfile
import threading
import unittest
from types import SimpleNamespace
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / 'toolchain'))
@@ -26,6 +27,7 @@ class DummyBridge:
self.calls.append((command, arguments))
if command == 'snapshot': return {'state': self.state}
if command == 'console_print': return {'printed': True}
if command == 'memory': return {'hex': '00' * arguments['length']}
raise AssertionError(command)
@@ -44,13 +46,20 @@ class DummySession:
return {'status': 'mapped', 'pc': 0x8100, 'link_address': 0x8100,
'sources': [{'file': '/src/main.c', 'line': 3}]}
def refresh(self):
return SimpleNamespace(generation=1, bank_pages={})
def break_line(self, filename, line, enabled=True):
if line == 99:
raise SessionError('нет адреса')
identifier = self.next_id
self.next_id += 1
return {'id': identifier, 'backend_ids': [identifier],
'conditions': [], 'locations': [{'line': line}], 'enabled': enabled}
'conditions': [], 'locations': [{'line': line, 'link_address': 0x8100}],
'enabled': enabled}
def break_function(self, name, enabled=True):
return self.break_line('/src/main.c', 3, enabled=enabled)
def clear_breakpoint(self, identifier):
self.cleared.append(identifier)
@@ -189,6 +198,39 @@ class ServerTests(unittest.TestCase):
server.close()
controller.close()
def test_individual_breakpoints_keep_owner_and_source_step_priority(self):
session = DummySession()
controller = SessionController(session)
try:
mine = controller.call('break_line',
{'file': '/src/main.c', 'line': 3, 'owner': 'mcp:a'})
other = controller.call('break_function', {'name': 'main', 'owner': 'mcp:b'})
self.assertTrue(controller._has_stop_breakpoint(session.where()))
with self.assertRaisesRegex(SessionError, 'не принадлежит'):
controller.call('clear_breakpoint', {'id': other['id'], 'owner': 'mcp:a'})
self.assertEqual(controller.call('clear_owned_breakpoints', {'owner': 'mcp:a'}),
{'cleared': [mine['id']]})
self.assertEqual(session.cleared, [mine['id']])
self.assertIn(other['id'], controller.breakpoint_info)
controller.call('clear_breakpoint', {'id': other['id'], 'owner': 'mcp:b'})
self.assertFalse(controller.breakpoint_info)
finally:
controller.close()
def test_read_memory_is_bounded_and_generation_tied(self):
session = DummySession()
controller = SessionController(session)
try:
result = controller.call('read_memory', {'address': 0xfffe, 'length': 2})
self.assertEqual(result, {'address': 0xfffe, 'length': 2, 'hex': '0000',
'generation': 1, 'bank_pages': {}})
with self.assertRaisesRegex(SessionError, 'за 64 КБ'):
controller.call('read_memory', {'address': 0xffff, 'length': 2})
with self.assertRaisesRegex(SessionError, 'длина 1..256'):
controller.call('read_memory', {'address': 0, 'length': 257})
finally:
controller.close()
def test_idle_snapshot_detects_invalidation(self):
session = DummySession()
controller = SessionController(session)