Testing¶
SimVX provides headless testing tools for game logic, UI widgets, and input — no GPU required.
For visual/pixel-level testing with the Vulkan renderer, see Visual Testing.
SceneRunner¶
SceneRunner drives the scene tree without a window. Load a node, advance frames, and inspect state:
from simvx.core.testing import SceneRunner
runner = SceneRunner(screen_size=(800, 600))
runner.load(MyGameScene())
runner.advance_frames(10)
player = runner.find("Player") # Find by name
enemies = runner.find_all(Enemy) # Find all of a type
assert player.health > 0
API¶
SceneRunner(screen_size=(800, 600))
runner.load(root_node) -> SceneRunner # Chainable
runner.advance_frames(count=1, dt=None) -> SceneRunner
runner.advance_time(seconds, dt=None) -> SceneRunner
runner.find(name_or_type, recursive=True) # str or type
runner.find_all(node_type) -> list[Node]
runner.snapshot() -> dict # Capture scene state
runner.root # Root node
runner.frame_count # Frames processed
runner.elapsed_time # Seconds elapsed
InputSimulator¶
InputSimulator drives the engine’s Input singleton directly — the same path as real platform adapters:
from simvx.core.testing import InputSimulator, SceneRunner
from simvx.core import Key, MouseButton
sim = InputSimulator()
runner = SceneRunner()
runner.load(MyScene())
sim.tap_key(Key.SPACE) # Press + release
runner.advance_frames(1)
assert runner.find("Player").jumped
sim.click((400, 300)) # Left-click at position
runner.advance_frames(1)
sim.move_mouse(500, 400) # Move cursor
sim.press_key(Key.W) # Hold key
runner.advance_frames(60) # Hold for 60 frames
sim.release_key(Key.W)
sim.reset() # Clear all pressed state
API¶
Method |
Description |
|---|---|
|
Hold a key down |
|
Release a key |
|
Press and release |
|
Press mouse button |
|
Release mouse button |
|
Press + release at position |
|
Move cursor |
|
Scroll wheel |
|
Clear all pressed state |
Scene Snapshots¶
Compare scene state before and after an operation:
from simvx.core.testing import SceneRunner, scene_diff
runner = SceneRunner().load(MyScene())
before = runner.snapshot()
runner.advance_frames(100)
after = runner.snapshot()
changes = scene_diff(before, after)
for change in changes:
print(change) # e.g. "Player.position: (0, 0) -> (100, 50)"
Describe¶
scene_describe() prints a human-readable tree of all nodes with their properties:
from simvx.core.testing import scene_describe
print(scene_describe(runner.root))
# MyScene
# Player (Node2D) position=(100, 50) health=3
# Sprite (Sprite2D)
# Enemy (Node2D) position=(400, 300)
UITestHarness¶
For widget-level testing without a GPU. Wraps a root Control with a mock scene tree and draw-command logger:
from simvx.core.ui.testing import UITestHarness
from simvx.core import Button, VBoxContainer
root = VBoxContainer()
btn = Button(text="Click Me")
root.add_child(btn)
harness = UITestHarness(root)
harness.click(btn) # Click by widget reference
assert btn.pressed_count > 0
harness.type_text("Hello") # Type into focused widget
harness.press_key("escape")
harness.scroll(dy=-3)
Draw Assertions¶
UITestHarness captures all draw commands in a DrawLog. Use it to verify rendering without a GPU:
harness.tick()
log = harness.draw_log
assert log.has_text("Click Me")
assert log.has_text_containing("Score")
texts = log.texts() # All rendered text strings
rects = log.rects_at(100, 50) # Rects drawn at a point
Utilities¶
Function/Class |
Description |
|---|---|
|
List of human-readable changes between snapshots |
|
Tree-format string of all nodes and properties |
|
UI-focused tree format |
|
Dict of |
|
Total node count |
|
Measure frame times ( |
API Reference¶
See simvx.core.testing and simvx.core.ui.testing for the complete testing API.