hallway8 / tests /test_emulation.py
alvations's picture
Deploy Hallway 8 (multi-arc memory game)
195f338 verified
Raw
History Blame Contribute Delete
3.09 kB
# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 alvations (Melon Lab)
"""Browser emulation test: drives the real client in headless Chromium.
Unit tests cover the server's math and payloads; this covers the *client*
rendering that only a browser exercises: reduced-motion rendering, Act 2 touch
verbs and the flavour line, the inspect aside, and the alternate-ending
achievement gate. The assertions live in ``tests/emulation.cjs`` (Playwright);
this wrapper boots the app on a free port and runs them.
It is **opt-in and skips gracefully**: it does nothing unless ``H8_EMU=1`` is
set and Node + Playwright + Chromium are present, so the fast default suite
(``python tests/test_game.py``) and browserless CI stay green. Run the full
thing with::
H8_EMU=1 python tests/test_game.py
"""
import os
import shutil
import socket
import subprocess
import sys
import time
import urllib.request
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
HARNESS = os.path.join(ROOT, "tests", "emulation.cjs")
CHROMIUM = os.environ.get(
"H8_CHROMIUM", "/opt/pw-browsers/chromium-1194/chrome-linux/chrome")
NODE_PATH = os.environ.get("NODE_PATH", "/opt/node22/lib/node_modules")
def _free_port():
s = socket.socket()
s.bind(("127.0.0.1", 0))
port = s.getsockname()[1]
s.close()
return port
def _wait_up(url, timeout=20):
deadline = time.time() + timeout
while time.time() < deadline:
try:
urllib.request.urlopen(url, timeout=1)
return True
except Exception:
time.sleep(0.3)
return False
def _skip(reason):
print(f" (skip test_emulation: {reason})")
def test_emulation():
if os.environ.get("H8_EMU") not in ("1", "true", "yes"):
return _skip("set H8_EMU=1 to run the browser emulation checks")
node = shutil.which("node")
if not node:
return _skip("node not found")
if not os.path.exists(CHROMIUM):
return _skip(f"chromium not found at {CHROMIUM}")
pw = os.path.join(NODE_PATH, "playwright")
if not os.path.isdir(pw):
return _skip("playwright module not found")
port = _free_port()
env = dict(os.environ, PORT=str(port), HOST="127.0.0.1")
app = subprocess.Popen([sys.executable, os.path.join(ROOT, "app.py")],
cwd=ROOT, env=env,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
url = f"http://127.0.0.1:{port}"
assert _wait_up(url), "app did not start"
proc = subprocess.run(
[node, HARNESS, url, CHROMIUM],
cwd=ROOT, env=dict(env, NODE_PATH=NODE_PATH),
capture_output=True, text=True, timeout=300)
sys.stdout.write(proc.stdout)
if proc.returncode == 2:
return _skip("harness reported environment unavailable")
assert proc.returncode == 0, (
"emulation checks failed:\n" + proc.stdout + proc.stderr)
finally:
app.terminate()
try:
app.wait(timeout=5)
except Exception:
app.kill()