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

press_key(key)

Hold a key down

release_key(key)

Release a key

tap_key(key)

Press and release

press_mouse(button=1, position=None)

Press mouse button

release_mouse(button=1)

Release mouse button

click(position, button=1)

Press + release at position

move_mouse(x, y)

Move cursor

scroll(dx=0, dy=-1)

Scroll wheel

reset()

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

scene_diff(before, after)

List of human-readable changes between snapshots

scene_describe(root)

Tree-format string of all nodes and properties

ui_describe(root)

UI-focused tree format

NodeCounter.count(root)

Dict of {type_name: count}

NodeCounter.total(root)

Total node count

FrameTimer

Measure frame times (average_ms, max_ms, fps)

API Reference

See simvx.core.testing and simvx.core.ui.testing for the complete testing API.