docs(agents): tooling + architecture-upkeep rules, with tool tests (#24)
Merged PR #24: tooling + architecture-upkeep rules, tool tests Co-authored-by: Zwitschi <zwitschi82@gmail.com>
This commit was merged in pull request #24.
This commit is contained in:
1 parent
420f863999
commit
68d8745534
10 files changed
+233
-17
No files matched your search
@@ -0,0 +1,26 @@
|
||||
# Tooling tests
|
||||
|
||||
Standard-library `unittest` tests for the host-side tools in `Tools/`. They never start Blender, Unreal, or the game; anything that needs those processes is verified by the tool itself and recorded in `Docs/release-checks.md` instead.
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```bash
|
||||
python3 -m pip install -r requirements-tools.txt
|
||||
python3 -m unittest discover -s Tools/tests -t Tools -p 'test_*.py'
|
||||
```
|
||||
|
||||
The `-t Tools` top-level directory is required: the tests import the tool modules by their plain names (`import input_emulation`), which is why `Tools/tests/` carries an `__init__.py`.
|
||||
|
||||
Individual test:
|
||||
|
||||
```bash
|
||||
python3 -m unittest Tools.tests.test_input_emulation -v
|
||||
```
|
||||
|
||||
## What belongs here
|
||||
|
||||
Test the **pure** parts — argument parsing, key/coordinate encoding, geometry maths, licence gating, report formatting, decision classification. Network, Blender and Unreal calls stay out: a test that needs a paid API, a display, or an engine process is a manual check, not a unit test.
|
||||
|
||||
When a tool's logic is buried in `main()`, extract it into a function so it can be tested directly. That is a tooling improvement, not test scaffolding.
|
||||
|
||||
Geo (`Geo/tools/`) and Blender (`Art/blender/*/`) builders are tested the same way once their pure parts are extracted; note the module path in the test.
|
||||
@@ -0,0 +1 @@
|
||||
"""Package marker so `python3 -m unittest Tools.tests.<module>` resolves."""
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Tests for Geo/tools/block_height_report.py::classify_height.
|
||||
|
||||
The Geo tools live outside `Tools/`, so the test adds `Geo/tools` to `sys.path`
|
||||
before importing the module by name.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
sys.path.insert(0, os.path.join(ROOT, "Geo", "tools"))
|
||||
|
||||
import block_height_report as bhr # noqa: E402
|
||||
|
||||
|
||||
class ClassifyHeightTest(unittest.TestCase):
|
||||
def test_height_tag_wins(self):
|
||||
height, source = bhr.classify_height({"building": "yes", "height": "18.1"})
|
||||
self.assertEqual(height, 18.1)
|
||||
self.assertEqual(source, "height tag")
|
||||
|
||||
def test_height_tag_beats_levels(self):
|
||||
height, _ = bhr.classify_height(
|
||||
{"building": "yes", "height": "12.5", "building:levels": "4"})
|
||||
self.assertEqual(height, 12.5)
|
||||
|
||||
def test_levels_derive_height(self):
|
||||
height, source = bhr.classify_height(
|
||||
{"building": "yes", "building:levels": "4"}, level_height=3.2)
|
||||
self.assertEqual(height, 12.8)
|
||||
self.assertIn("levels x", source)
|
||||
|
||||
def test_roof_is_not_a_building_volume(self):
|
||||
# building=roof at layer=1 is an elevated canopy, not a two-storey block.
|
||||
height, source = bhr.classify_height({"building": "roof", "layer": "1"})
|
||||
self.assertEqual(height, 0.0)
|
||||
self.assertIn("not a building volume (roof)", source)
|
||||
|
||||
def test_transit_shelter_is_not_a_building_volume(self):
|
||||
height, source = bhr.classify_height(
|
||||
{"building": "yes", "amenity": "shelter", "shelter_type": "public_transport"})
|
||||
self.assertEqual(height, 0.0)
|
||||
self.assertIn("not a building volume (shelter)", source)
|
||||
|
||||
def test_missing_data_falls_back_to_a_flagged_guess(self):
|
||||
height, source = bhr.classify_height({"building": "yes"})
|
||||
self.assertEqual(height, 2 * bhr.LEVEL_HEIGHT)
|
||||
self.assertTrue(source.startswith("GUESS"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,57 @@
|
||||
"""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()
|
||||
Reference in new issue
Block a user