Files
gig-line/Tools/tests/test_input_emulation.py
T
zwitschi 5be6c84b37 docs(agents): tooling-improvement and architecture-upkeep rules; add tool tests
Researched the sibling project's tooling discipline (tools/README inventory,
tools/tests with stdlib unittest, check_docs gate, clean-worktree rule, paid-API
tools human-triggered) and encoded it for this repository:

AGENTS.md gains two sections.
- 'Python and shell tooling': fix the tool not the artefact; improve over fork;
  one script one job; fail loudly; result files for Unreal commandlets; absolute
  paths into sandboxes; clean worktree for generators (.blend/.umap are
  read-only on checkout); network/paid tools human-triggered with 429 back-off;
  measure don't assert; document every tool in the README inventory; test the
  pure parts.
- 'Architecture documentation': Docs/Architecture is arc42 in Markdown; a table
  mapping each kind of change to the section that must be updated; describe what
  exists, ADRs append-only, keep architecture.md in sync, prettier --check.

Made the referenced scaffolding real rather than aspirational:
- Tools/tests/ (README + __init__) with 12 passing unittest tests covering
  input_emulation's ioctl/key-code encoding and block_height_report's height
  classification.
- requirements-tools.txt (requests).
- Extracted classify_height() out of block_height_report.main() so the roof/
  shelter rule is testable — the rule itself came from the v0.1.2 finding.
- Tools/README: tests section, input-injection rows, and the removed TEMP-QA row
  replaced by the real cvar/self-test description.
- Docs/Architecture 05 + architecture.md: tools table extended (input injection,
  tests) and a link to the tool rules; kept in sync per the new rule.
2026-11-08 16:25:26 +01:00

58 lines
1.9 KiB
Python

"""Tests for Tools/input_emulation.py — the pure ioctl/key-code parts.
The device path itself needs /dev/uinput, so only the encoding helpers are
covered here; the end-to-end injection is verified by running the tool (see
Docs/release-checks.md and MEMORY.md).
"""
import unittest
import input_emulation as ie
class IocEncodingTest(unittest.TestCase):
"""_IOC must match the Linux ioctl bit layout."""
def test_layout_constants(self):
self.assertEqual(ie._IOC_NRSHIFT, 0)
self.assertEqual(ie._IOC_TYPESHIFT, 8)
self.assertEqual(ie._IOC_SIZESHIFT, 16)
self.assertEqual(ie._IOC_DIRSHIFT, 30)
def test_known_values(self):
# UI_SET_EVBIT = _IOW('U', 100, int) -> 0x40045564 on Linux.
self.assertEqual(ie.UI_SET_EVBIT, 0x40045564)
# UI_DEV_CREATE = _IO('U', 1) -> 0x5501.
self.assertEqual(ie.UI_DEV_CREATE, 0x5501)
def test_type_accepts_char_or_int(self):
self.assertEqual(ie._ioc(1, 4, "U", 100), ie._ioc(1, 4, ord("U"), 100))
class KeyCodeTest(unittest.TestCase):
"""KEYCODES must hold the Linux input-event codes the demo uses."""
EXPECTED = {
"w": 17, "s": 31, "a": 30, "d": 32,
"space": 57, "r": 19, "esc": 1,
}
def test_demo_controls(self):
for name, code in self.EXPECTED.items():
self.assertEqual(ie.KEYCODES[name], code, f"{name} code")
def test_lookup_is_case_insensitive(self):
dev = ie.InputDevice.__new__(ie.InputDevice) # no device opened
self.assertEqual(dev._key_code("W"), 17)
self.assertEqual(dev._key_code("SPACE"), 57)
self.assertEqual(dev._key_code(19), 19) # a raw code passes through
def test_unknown_key_raises(self):
dev = ie.InputDevice.__new__(ie.InputDevice)
with self.assertRaises(ie.UInputError):
dev._key_code("not-a-key")
if __name__ == "__main__":
unittest.main()