Support bounded machine instruction step counts in MCP

This commit is contained in:
Александр Петров
2026-09-17 23:22:37 +03:00
parent e8189b137a
commit 259c782190
13 changed files with 62 additions and 10 deletions
+4
View File
@@ -109,6 +109,10 @@ async def probe() -> None:
after_dap = await call('session_status')
if after_dap['phase'] != 'ready' or after_dap['session_id'] != status['session_id']:
raise RuntimeError('DAP disconnect завершил MCP-owned MAME')
stepped = await call('step_instruction', {'count': 3})
if stepped['pc'] == location['pc'] or \
(await call('session_status'))['running']:
raise RuntimeError('Три машинных шага не остановились: ' + repr(stepped))
source = str(ROOT / 'tests/hello/hello.c')
before = await call('set_line_breakpoint', {'file': source, 'line': 62})
points = await call('list_breakpoints')
+10
View File
@@ -55,6 +55,16 @@ class McpAdapterTests(unittest.TestCase):
self.client.step_source('back')
self.assertEqual(len(self.calls), before)
def test_machine_step_count_is_validated_before_rpc(self):
before = len(self.calls)
for count in (0, 65, True):
with self.assertRaisesRegex(SessionError, '1..64'):
self.client.step_instruction(count)
self.assertEqual(len(self.calls), before)
self.client.step_instruction(3)
self.assertEqual(self.calls[-1][1], 'step')
self.assertEqual(self.calls[-1][2]['count'], 3)
def test_press_key_releases_shift_after_snapshot_error(self):
calls = []
+19 -1
View File
@@ -111,13 +111,16 @@ class StepBridge:
def __init__(self, owner):
self.owner = owner
self.calls = []
self.step_counts = []
def close(self): pass
def request(self, command, **arguments):
self.calls.append(command)
if command in ('step', 'step_over', 'step_out'):
self.owner.index = min(self.owner.index + 1,
count = arguments.get('count', 1) if command == 'step' else 1
self.step_counts.append(count)
self.owner.index = min(self.owner.index + count,
len(self.owner.locations) - 1)
return {'accepted': True}
if command == 'snapshot':
@@ -482,6 +485,21 @@ class ServerTests(unittest.TestCase):
finally:
controller.close()
def test_instruction_step_count_is_bounded_and_forwarded(self):
session = StepSession()
controller = SessionController(session)
try:
with self.assertRaisesRegex(SessionError, '1..64'):
controller.call('step', {'count': 0, 'owner': 'mcp:test'})
for bad in (65, 1.5, True):
with self.assertRaisesRegex(SessionError, '1..64'):
controller.call('step', {'count': bad, 'owner': 'mcp:test'})
result = controller.call('step', {'count': 2, 'owner': 'mcp:test'})
self.assertEqual(result['sources'][0]['line'], 4)
self.assertEqual(session.bridge.step_counts, [2])
finally:
controller.close()
def test_source_step_preserves_user_breakpoint_on_same_line(self):
session = StepSession()
controller = SessionController(session)