mcqemu/tests/test_display.py

122 lines
4.6 KiB
Python

"""See & drive tools with fake QMP; screendump writes a synthetic PNG."""
import struct
import pytest
from fastmcp import Client
from fastmcp.exceptions import ToolError
from conftest import FakeQMPClient, result_data, write_registry
from mcqemu.server import mcp
from test_lifecycle import seeded_record
def fake_png(width: int, height: int) -> bytes:
"""Just enough PNG for signature + IHDR dimension parsing."""
return (
b"\x89PNG\r\n\x1a\n"
+ b"\x00\x00\x00\x0dIHDR"
+ struct.pack(">II", width, height)
+ b"\x08\x06\x00\x00\x00"
+ b"\x00" * 16
)
def arm_screendump(width=640, height=480):
def handler(args):
with open(args["filename"], "wb") as f:
f.write(fake_png(width, height))
return {}
FakeQMPClient.responses["screendump"] = handler
async def test_screenshot_returns_png_image(dirs, fake_qmp):
arm_screendump()
write_registry(dirs, seeded_record(dirs, "vm1"))
async with Client(mcp) as client:
result = await client.call_tool("vm_screenshot", {"name": "vm1"})
block = result.content[0]
assert block.type == "image"
assert block.mimeType == "image/png"
call = next(a for c, a in FakeQMPClient.calls if c == "screendump")
assert call["format"] == "png"
async def test_send_keys_chords(dirs, fake_qmp):
FakeQMPClient.responses["send-key"] = {}
write_registry(dirs, seeded_record(dirs, "vm1"))
async with Client(mcp) as client:
data = result_data(
await client.call_tool(
"vm_send_keys", {"name": "vm1", "keys": ["ctrl-alt-f2", "enter"], "delay_ms": 0}
)
)
assert data["keys_sent"] == 2
sent = [a["keys"] for c, a in FakeQMPClient.calls if c == "send-key"]
assert sent[0] == [
{"type": "qcode", "data": "ctrl"},
{"type": "qcode", "data": "alt"},
{"type": "qcode", "data": "f2"},
]
assert sent[1] == [{"type": "qcode", "data": "ret"}]
async def test_send_keys_unknown_key(dirs, fake_qmp):
write_registry(dirs, seeded_record(dirs, "vm1"))
async with Client(mcp) as client:
with pytest.raises(ToolError, match="Unknown key"):
await client.call_tool("vm_send_keys", {"name": "vm1", "keys": ["warpdrive"]})
async def test_type_text_with_shift_and_enter(dirs, fake_qmp):
FakeQMPClient.responses["send-key"] = {}
write_registry(dirs, seeded_record(dirs, "vm1"))
async with Client(mcp) as client:
data = result_data(
await client.call_tool(
"vm_type_text", {"name": "vm1", "text": "Hi!", "enter": True, "delay_ms": 0}
)
)
assert data["characters_typed"] == 4 # H, i, !, Enter
sent = [a["keys"] for c, a in FakeQMPClient.calls if c == "send-key"]
assert sent[0] == [{"type": "qcode", "data": "shift"}, {"type": "qcode", "data": "h"}]
assert sent[1] == [{"type": "qcode", "data": "i"}]
assert sent[2] == [{"type": "qcode", "data": "shift"}, {"type": "qcode", "data": "1"}]
assert sent[3] == [{"type": "qcode", "data": "ret"}]
async def test_click_scales_to_abs_space(dirs, fake_qmp):
arm_screendump(width=640, height=480)
FakeQMPClient.responses["input-send-event"] = {}
write_registry(dirs, seeded_record(dirs, "vm1"))
async with Client(mcp) as client:
data = result_data(await client.call_tool("vm_click", {"name": "vm1", "x": 320, "y": 240}))
assert "left at (320, 240)" in data["clicked"]
events = [a["events"] for c, a in FakeQMPClient.calls if c == "input-send-event"]
move = events[0]
assert move[0]["data"] == {"axis": "x", "value": int(320 * 32767 / 639)}
assert move[1]["data"] == {"axis": "y", "value": int(240 * 32767 / 479)}
assert events[1][0]["data"] == {"down": True, "button": "left"}
assert events[2][0]["data"] == {"down": False, "button": "left"}
async def test_click_outside_display_rejected(dirs, fake_qmp):
arm_screendump(width=640, height=480)
write_registry(dirs, seeded_record(dirs, "vm1"))
async with Client(mcp) as client:
with pytest.raises(ToolError, match="outside"):
await client.call_tool("vm_click", {"name": "vm1", "x": 9999, "y": 10})
async def test_serial_read_tail(dirs, fake_qmp):
record = seeded_record(dirs, "vm1", serial_log=str(dirs.state / "serial.log"))
(dirs.state / "serial.log").write_text("\n".join(f"line{i}" for i in range(100)))
write_registry(dirs, record)
async with Client(mcp) as client:
data = result_data(
await client.call_tool("vm_serial_read", {"name": "vm1", "tail_lines": 3})
)
assert data["total_lines"] == 100
assert data["tail"] == "line97\nline98\nline99"