MCP server for AI-assisted debugging of DOS binaries via GDB protocol. Features: - GDB remote protocol client for DOSBox-X debugging - 16 debugging tools: launch, attach, breakpoint management, registers, memory read/write, disassemble, step, continue, etc. - Docker container with DOSBox-X for consistent environment - Support for DOS segment:offset addressing - Comprehensive test suite (49 tests) Primary use case: Reverse engineering the unpublished Bezier algorithm in RIPTERM.EXE for the RIPscrip graphics protocol project.
192 lines
5.5 KiB
Python
192 lines
5.5 KiB
Python
"""Tests for type definitions."""
|
|
|
|
import pytest
|
|
|
|
from dosbox_mcp.types import (
|
|
Breakpoint,
|
|
DisassemblyLine,
|
|
DOSBoxStatus,
|
|
MemoryRegion,
|
|
Registers,
|
|
StopEvent,
|
|
StopReason,
|
|
)
|
|
|
|
|
|
class TestRegisters:
|
|
"""Tests for Registers type."""
|
|
|
|
def test_16bit_aliases(self):
|
|
"""Test 16-bit register aliases."""
|
|
regs = Registers(eax=0x12345678, ebx=0xAABBCCDD)
|
|
assert regs.ax == 0x5678
|
|
assert regs.bx == 0xCCDD
|
|
|
|
def test_physical_address_calculation(self):
|
|
"""Test segment:offset to physical address."""
|
|
regs = Registers(cs=0x1000, eip=0x0100, ss=0x2000, esp=0x0200)
|
|
|
|
assert regs.cs_ip == 0x10100 # (0x1000 << 4) + 0x100
|
|
assert regs.ss_sp == 0x20200 # (0x2000 << 4) + 0x200
|
|
|
|
def test_flag_checking(self):
|
|
"""Test CPU flag checking."""
|
|
# EFLAGS with zero flag (bit 6) and carry flag (bit 0) set
|
|
regs = Registers(eflags=0x41) # bits 0 and 6
|
|
|
|
assert regs.flag_set('cf') is True
|
|
assert regs.flag_set('carry') is True
|
|
assert regs.flag_set('zf') is True
|
|
assert regs.flag_set('zero') is True
|
|
assert regs.flag_set('sf') is False
|
|
assert regs.flag_set('sign') is False
|
|
|
|
def test_flag_unknown(self):
|
|
"""Test unknown flag name."""
|
|
regs = Registers()
|
|
with pytest.raises(ValueError):
|
|
regs.flag_set('unknown_flag')
|
|
|
|
def test_to_dict(self):
|
|
"""Test dictionary serialization."""
|
|
regs = Registers(eax=0x1234, cs=0x100, eip=0x200, eflags=0x41)
|
|
d = regs.to_dict()
|
|
|
|
assert d['eax'] == '00001234'
|
|
assert d['ax'] == '1234'
|
|
assert d['cs'] == '0100'
|
|
assert d['cs:ip'] == '0100:0200'
|
|
assert d['flags']['carry'] is True
|
|
assert d['flags']['zero'] is True
|
|
|
|
|
|
class TestBreakpoint:
|
|
"""Tests for Breakpoint type."""
|
|
|
|
def test_creation(self):
|
|
"""Test breakpoint creation."""
|
|
bp = Breakpoint(id=1, address=0x10100, original="1000:0100")
|
|
assert bp.id == 1
|
|
assert bp.address == 0x10100
|
|
assert bp.enabled is True
|
|
assert bp.hit_count == 0
|
|
|
|
def test_to_dict(self):
|
|
"""Test dictionary serialization."""
|
|
bp = Breakpoint(id=1, address=0x10100, hit_count=5, original="1000:0100")
|
|
d = bp.to_dict()
|
|
|
|
assert d['id'] == 1
|
|
assert d['address'] == '10100'
|
|
assert d['hit_count'] == 5
|
|
|
|
|
|
class TestStopEvent:
|
|
"""Tests for StopEvent type."""
|
|
|
|
def test_breakpoint_event(self):
|
|
"""Test breakpoint stop event."""
|
|
event = StopEvent(
|
|
reason=StopReason.BREAKPOINT,
|
|
address=0x10100,
|
|
signal=5,
|
|
breakpoint_id=1
|
|
)
|
|
|
|
d = event.to_dict()
|
|
assert d['reason'] == 'breakpoint'
|
|
assert d['address'] == '10100'
|
|
assert d['breakpoint_id'] == 1
|
|
|
|
def test_step_event(self):
|
|
"""Test step stop event."""
|
|
event = StopEvent(reason=StopReason.STEP, address=0x10100)
|
|
|
|
d = event.to_dict()
|
|
assert d['reason'] == 'step'
|
|
|
|
|
|
class TestMemoryRegion:
|
|
"""Tests for MemoryRegion type."""
|
|
|
|
def test_hex_format(self):
|
|
"""Test hex format output."""
|
|
mem = MemoryRegion(address=0x100, data=b"\x90\x90\xcc")
|
|
assert mem.to_hex() == "9090cc"
|
|
|
|
def test_ascii_format(self):
|
|
"""Test ASCII format output."""
|
|
mem = MemoryRegion(address=0x100, data=b"Hello\x00World")
|
|
assert mem.to_ascii() == "Hello.World"
|
|
|
|
def test_to_dict(self):
|
|
"""Test dictionary serialization."""
|
|
mem = MemoryRegion(address=0x100, data=b"AB")
|
|
d = mem.to_dict(format="both")
|
|
|
|
assert d['address'] == '00100'
|
|
assert d['length'] == 2
|
|
assert d['hex'] == '4142'
|
|
assert d['ascii'] == 'AB'
|
|
|
|
|
|
class TestDisassemblyLine:
|
|
"""Tests for DisassemblyLine type."""
|
|
|
|
def test_to_dict(self):
|
|
"""Test dictionary serialization."""
|
|
line = DisassemblyLine(
|
|
address=0x10100,
|
|
bytes_hex="90",
|
|
mnemonic="NOP",
|
|
operands=""
|
|
)
|
|
d = line.to_dict()
|
|
|
|
assert d['address'] == '10100'
|
|
assert d['bytes'] == '90'
|
|
assert d['instruction'] == 'NOP'
|
|
|
|
def test_instruction_with_operands(self):
|
|
"""Test instruction with operands."""
|
|
line = DisassemblyLine(
|
|
address=0x10100,
|
|
bytes_hex="b80100",
|
|
mnemonic="MOV",
|
|
operands="AX, 0001"
|
|
)
|
|
d = line.to_dict()
|
|
|
|
assert d['instruction'] == 'MOV AX, 0001'
|
|
|
|
|
|
class TestDOSBoxStatus:
|
|
"""Tests for DOSBoxStatus type."""
|
|
|
|
def test_default_status(self):
|
|
"""Test default status values."""
|
|
status = DOSBoxStatus()
|
|
assert status.running is False
|
|
assert status.connected is False
|
|
assert status.breakpoints == []
|
|
|
|
def test_to_dict(self):
|
|
"""Test dictionary serialization."""
|
|
bp = Breakpoint(id=1, address=0x100)
|
|
status = DOSBoxStatus(
|
|
running=True,
|
|
connected=True,
|
|
host="localhost",
|
|
port=1234,
|
|
pid=12345,
|
|
breakpoints=[bp]
|
|
)
|
|
d = status.to_dict()
|
|
|
|
assert d['running'] is True
|
|
assert d['connected'] is True
|
|
assert d['host'] == "localhost"
|
|
assert d['port'] == 1234
|
|
assert d['pid'] == 12345
|
|
assert d['breakpoint_count'] == 1
|