# 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 {doc}`../graphics/testing`. ## SceneRunner `SceneRunner` drives the scene tree without a window. Load a node, advance frames, and inspect state: ```python 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 ```python 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: ```python 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: ```python 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: ```python 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: ```python 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: ```python 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 {py:mod}`simvx.core.testing` and {py:mod}`simvx.core.ui.testing` for the complete testing API.