mcdosbox-x/tests/test_types.py
Ryan Malloy 170eba0843 Initial implementation of DOSBox-X MCP Server
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.
2026-01-27 13:07:51 -07:00

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