tests
Browse files- test_xros.py +109 -0
test_xros.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""test_xros.py — end-to-end test of the spatial shell + world-state service.
|
| 2 |
+
|
| 3 |
+
Two layers:
|
| 4 |
+
1. Unit: drive the SpatialShell directly against a WorldState and assert
|
| 5 |
+
the world mutates correctly.
|
| 6 |
+
2. Integration: start the real WebSocket server, connect a client, run
|
| 7 |
+
shell commands over the wire, and confirm a second client observes the
|
| 8 |
+
world changes (the DiOS "single shared resource" claim).
|
| 9 |
+
|
| 10 |
+
Run: python test_xros.py
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import asyncio
|
| 14 |
+
import json
|
| 15 |
+
|
| 16 |
+
import websockets
|
| 17 |
+
|
| 18 |
+
from xros_world import WorldState
|
| 19 |
+
from xros_shell import SpatialShell
|
| 20 |
+
from xros_server import handle
|
| 21 |
+
from xros_client import WorldClient
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
async def test_shell_unit() -> None:
|
| 25 |
+
world = WorldState()
|
| 26 |
+
shell = SpatialShell(world)
|
| 27 |
+
|
| 28 |
+
out = await shell.execute("help")
|
| 29 |
+
assert "spawn" in out, out
|
| 30 |
+
|
| 31 |
+
out = await shell.execute("spawn panel --title Hello --color blue")
|
| 32 |
+
assert "spawned panel" in out, out
|
| 33 |
+
|
| 34 |
+
out = await shell.execute("spawn cube 1 2 0")
|
| 35 |
+
assert "spawned cube" in out, out
|
| 36 |
+
|
| 37 |
+
out = await shell.execute("ls")
|
| 38 |
+
assert "panel" in out and "cube" in out, out
|
| 39 |
+
|
| 40 |
+
objs = await world.snapshot()
|
| 41 |
+
assert len(objs) == 2, objs
|
| 42 |
+
cube = next(o for o in objs if o["type"] == "cube")
|
| 43 |
+
assert cube["pose"]["position"] == [1.0, 2.0, 0.0], cube
|
| 44 |
+
|
| 45 |
+
out = await shell.execute(f"move {cube['id']} 5 5 5")
|
| 46 |
+
assert "moved" in out, out
|
| 47 |
+
moved = await world.get(cube["id"])
|
| 48 |
+
assert moved.pose.position == [5.0, 5.0, 5.0]
|
| 49 |
+
|
| 50 |
+
out = await shell.execute("gen a clock for the wall")
|
| 51 |
+
assert "generated clock panel" in out, out
|
| 52 |
+
assert any(o["properties"].get("panel_type") == "clock" for o in await world.snapshot())
|
| 53 |
+
|
| 54 |
+
out = await shell.execute("clear")
|
| 55 |
+
assert "cleared 3" in out, out
|
| 56 |
+
assert await world.snapshot() == []
|
| 57 |
+
|
| 58 |
+
out = await shell.execute("bogus")
|
| 59 |
+
assert out.startswith("error: unknown command"), out
|
| 60 |
+
|
| 61 |
+
print("unit tests: OK")
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
async def test_integration() -> None:
|
| 65 |
+
world = WorldState()
|
| 66 |
+
shell = SpatialShell(world)
|
| 67 |
+
|
| 68 |
+
async def server():
|
| 69 |
+
async with websockets.serve(lambda ws: handle(ws, world, shell), "127.0.0.1", 8766):
|
| 70 |
+
await asyncio.Future()
|
| 71 |
+
|
| 72 |
+
srv = asyncio.create_task(server())
|
| 73 |
+
await asyncio.sleep(0.2)
|
| 74 |
+
|
| 75 |
+
uri = "ws://127.0.0.1:8766"
|
| 76 |
+
|
| 77 |
+
# Client A: the "terminal" — runs shell commands (filters world events).
|
| 78 |
+
a = WorldClient(uri)
|
| 79 |
+
await a.connect()
|
| 80 |
+
# Client B: the "world renderer" — observes every world event.
|
| 81 |
+
b = await websockets.connect(uri)
|
| 82 |
+
|
| 83 |
+
# A spawns a panel via the shell.
|
| 84 |
+
out = await a.command("spawn panel --title Hello")
|
| 85 |
+
assert "spawned panel" in out, out
|
| 86 |
+
|
| 87 |
+
# B should receive the spawn event (the shared-world claim).
|
| 88 |
+
event = json.loads(await asyncio.wait_for(b.recv(), timeout=5))
|
| 89 |
+
assert event["op"] == "spawn", event
|
| 90 |
+
assert event["object"]["type"] == "panel", event
|
| 91 |
+
|
| 92 |
+
# A lists the world.
|
| 93 |
+
out = await a.command("ls")
|
| 94 |
+
assert "panel" in out, out
|
| 95 |
+
|
| 96 |
+
await a.close()
|
| 97 |
+
await b.close()
|
| 98 |
+
srv.cancel()
|
| 99 |
+
print("integration test: OK (client B observed client A's spawn)")
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
async def main() -> None:
|
| 103 |
+
await test_shell_unit()
|
| 104 |
+
await test_integration()
|
| 105 |
+
print("\nALL TESTS PASSED")
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
if __name__ == "__main__":
|
| 109 |
+
asyncio.run(main())
|