McRogueFace/tests/api/test_api_windowed.py

211 lines
7.4 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""Test for the McRogueFace Game API in windowed mode.
Run with: cd build && ./mcrogueface --exec ../tests/api/test_api_windowed.py
Tests all API endpoints and verifies proper functionality.
"""
import sys
import time
import urllib.request
import json
import mcrfpy
# Force flush on print
import functools
print = functools.partial(print, flush=True)
def log(msg):
print(msg)
# Create a test scene with some UI elements
test_scene = mcrfpy.Scene("api_test")
ui = test_scene.children
# Add various interactive elements
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
# A button-like frame with click handler
button_frame = mcrfpy.Frame(pos=(50, 50), size=(200, 60), fill_color=(64, 64, 128))
button_frame.name = "play_button"
button_label = mcrfpy.Caption(text="Play Game", pos=(20, 15), font=font, fill_color=(255, 255, 255))
button_frame.children.append(button_label)
click_count = [0]
def on_button_click(pos, button, action):
if action == mcrfpy.InputState.PRESSED:
click_count[0] += 1
print(f"Button clicked! Count: {click_count[0]}")
button_frame.on_click = on_button_click
ui.append(button_frame)
# A second button
settings_frame = mcrfpy.Frame(pos=(50, 130), size=(200, 60), fill_color=(64, 128, 64))
settings_frame.name = "settings_button"
settings_label = mcrfpy.Caption(text="Settings", pos=(20, 15), font=font, fill_color=(255, 255, 255))
settings_frame.children.append(settings_label)
settings_frame.on_click = lambda pos, btn, action: print("Settings clicked")
ui.append(settings_frame)
# A caption without click (for display)
title = mcrfpy.Caption(text="API Test Scene", pos=(50, 10), font=font, fill_color=(255, 255, 0))
title.font_size = 24
ui.append(title)
# Status caption to show test progress
status = mcrfpy.Caption(text="Starting API tests...", pos=(50, 220), font=font, fill_color=(255, 255, 255))
ui.append(status)
# Activate scene
mcrfpy.current_scene = test_scene
# Start the API server
log("Starting API server...")
sys.path.insert(0, '../src/scripts')
from api import start_server
server = start_server(8765)
print("API server started on http://localhost:8765")
def run_api_tests(timer, runtime):
"""Run the API tests after scene is set up."""
print("\n=== Starting API Tests ===\n")
status.text = "Running tests..."
base_url = "http://localhost:8765"
all_passed = True
# Test 1: Health check
print("Test 1: Health check...")
try:
req = urllib.request.Request(f"{base_url}/health")
with urllib.request.urlopen(req, timeout=5) as response:
data = json.loads(response.read())
assert data["status"] == "ok", f"Expected 'ok', got '{data['status']}'"
print(f" PASS: Server healthy, version {data.get('version')}")
except Exception as e:
print(f" FAIL: {e}")
all_passed = False
# Test 2: Scene introspection
print("\nTest 2: Scene introspection...")
try:
req = urllib.request.Request(f"{base_url}/scene")
with urllib.request.urlopen(req, timeout=5) as response:
data = json.loads(response.read())
assert data["scene_name"] == "api_test", f"Expected 'api_test', got '{data['scene_name']}'"
print(f" PASS: Scene '{data['scene_name']}' with {data['element_count']} elements")
print(f" Viewport: {data['viewport']}")
for elem in data.get('elements', []):
print(f" - {elem['type']}: name='{elem.get('name', '')}' interactive={elem.get('interactive')}")
except Exception as e:
print(f" FAIL: {e}")
all_passed = False
# Test 3: Affordances
print("\nTest 3: Affordance extraction...")
try:
req = urllib.request.Request(f"{base_url}/affordances")
with urllib.request.urlopen(req, timeout=5) as response:
data = json.loads(response.read())
affordances = data["affordances"]
print(f" Found {len(affordances)} affordances:")
for aff in affordances:
print(f" ID={aff['id']} type={aff['type']} label='{aff.get('label')}' actions={aff.get('actions')}")
if len(affordances) >= 2:
print(f" PASS")
else:
print(f" WARN: Expected at least 2 affordances")
except Exception as e:
print(f" FAIL: {e}")
all_passed = False
# Test 4: Metadata
print("\nTest 4: Metadata...")
try:
req = urllib.request.Request(f"{base_url}/metadata")
with urllib.request.urlopen(req, timeout=5) as response:
data = json.loads(response.read())
print(f" Game: {data.get('game_name')}")
print(f" Current scene: {data.get('current_scene')}")
print(f" PASS")
except Exception as e:
print(f" FAIL: {e}")
all_passed = False
# Test 5: Input - click affordance by label
print("\nTest 5: Click affordance by label...")
try:
req = urllib.request.Request(
f"{base_url}/input",
data=json.dumps({
"action": "click_affordance",
"label": "Play Game" # Matches the button text
}).encode('utf-8'),
headers={"Content-Type": "application/json"}
)
with urllib.request.urlopen(req, timeout=5) as response:
data = json.loads(response.read())
if data.get("success"):
print(f" PASS: Clicked affordance '{data.get('affordance_label')}' at ({data.get('x'):.0f}, {data.get('y'):.0f})")
else:
print(f" FAIL: {data}")
all_passed = False
except Exception as e:
print(f" FAIL: {e}")
all_passed = False
# Test 6: Screenshot (base64)
print("\nTest 6: Screenshot...")
try:
req = urllib.request.Request(f"{base_url}/screenshot?format=base64")
with urllib.request.urlopen(req, timeout=5) as response:
data = json.loads(response.read())
if "image" in data and data["image"].startswith("data:image/png;base64,"):
img_size = len(data["image"])
print(f" PASS: Got base64 image ({img_size} chars)")
else:
print(f" FAIL: Invalid image data")
all_passed = False
except Exception as e:
print(f" FAIL: {e}")
all_passed = False
# Test 7: Wait endpoint (quick check)
print("\nTest 7: Wait endpoint...")
try:
req = urllib.request.Request(f"{base_url}/wait?timeout=1")
with urllib.request.urlopen(req, timeout=3) as response:
data = json.loads(response.read())
print(f" PASS: Scene hash: {data.get('hash')}")
except Exception as e:
print(f" FAIL: {e}")
all_passed = False
if all_passed:
print("\n=== All API Tests Passed ===\n")
status.text = "All tests PASSED!"
status.fill_color = (0, 255, 0)
else:
print("\n=== Some Tests Failed ===\n")
status.text = "Some tests FAILED"
status.fill_color = (255, 0, 0)
print("Press ESC to exit, or interact with the scene...")
print(f"API still running at {base_url}")
# Add key handler to exit
def on_key(key, action):
if key == mcrfpy.Key.ESCAPE and action == mcrfpy.InputState.PRESSED:
mcrfpy.exit()
test_scene.on_key = on_key
# Run tests after a short delay to let rendering settle
test_timer = mcrfpy.Timer("api_test", run_api_tests, 1000)