OXYGENHUB
# 8ac19619e24b retro-remote-debug-controller / conformance / test_conformance.py
2.7 KiB · Python · last changed Add conformance suite; client: raw PPM screenshot + PPM parser 3 months ago source blame history raw
8ac19619 Andy McCall ↶ 1
"""Contract conformance tests (SPEC.md 0.1.x). Platform-agnostic: framebuffer
2
dimensions come from the PPM header, addresses use $0000, and `bank` is
3
exercised but banking is not required."""
4
import urllib.error
5
import urllib.request
6
7
from emu_control import parse_ppm_header
8
9
10
def _raw(emu, method, path):
11
    """Raw request returning (status_code, body) without the client's wrapping,
12
    so we can assert on 4xx status codes."""
13
    req = urllib.request.Request(emu.base + path, method=method)
14
    try:
15
        with urllib.request.urlopen(req, timeout=emu.timeout) as r:
16
            return r.status, r.read()
17
    except urllib.error.HTTPError as e:
18
        return e.code, e.read()
19
20
21
def test_status_contract(emu):
22
    st = emu.status()
23
    for k in ("contract", "emulator", "platform", "frame", "paused", "running"):
24
        assert k in st, f"missing /status.{k}"
25
    assert str(st["contract"]).split(".")[0] == "0", "unexpected contract MAJOR"
26
    assert isinstance(st["platform"], str) and st["platform"]
27
    assert isinstance(st["frame"], int)
28
    assert isinstance(st["paused"], bool)
29
30
31
def test_regs_object(emu):
32
    r = emu.regs()
33
    assert isinstance(r, dict) and r, "regs must be a non-empty JSON object"
34
    assert all(isinstance(v, int) for v in r.values()), "register values must be ints"
35
36
37
def test_mem_exact_length(emu):
38
    assert len(emu.mem(0x0000, 32)) == 32
39
    assert len(emu.mem(0x0000, 256)) == 256
40
    assert len(emu.mem(0x0000, 0)) == 0
41
42
43
def test_mem_bank_param_returns_length(emu):
44
    # bank is optional (ignored on flat platforms) but must still honour len.
45
    assert len(emu.mem(0x0000, 8, bank=0)) == 8
46
47
48
def test_screenshot_ppm(emu):
49
    data = emu.screenshot_ppm()
50
    w, h, maxval, off = parse_ppm_header(data)
51
    assert w > 0 and h > 0, f"bad dimensions {w}x{h}"
52
    assert maxval == 255
53
    assert len(data) == off + 3 * w * h, "PPM body size mismatches dimensions"
54
55
56
def test_step_is_deterministic(emu):
57
    emu.pause()
58
    try:
59
        a = emu.frame()
60
        b = emu.step(2); c = emu.frame()
61
        assert b == c, "/step must return the halted frame counter"
62
        d = emu.step(2); e = emu.frame()
63
        assert d == e
64
        assert c - a > 0, "/step must advance the frame counter"
65
        assert (c - a) == (e - c), "equal steps must advance by equal amounts"
66
    finally:
67
        emu.resume()
68
69
70
def test_pause_resume_toggle(emu):
71
    emu.pause()
72
    assert emu.status()["paused"] is True
73
    emu.resume()
74
    assert emu.status()["paused"] is False
75
76
77
def test_unknown_path_404(emu):
78
    code, _ = _raw(emu, "GET", "/no-such-endpoint")
79
    assert code == 404
80
81
82
def test_step_requires_post(emu):
83
    code, _ = _raw(emu, "GET", "/step?frames=1")
84
    assert code == 405