simvx.core.testing¶
SimVX Testing API – headless scene testing, input simulation, and diagnostics.
Usage: from simvx.core.testing import SceneRunner, InputSimulator, scene_diff
# Test a game scene
runner = SceneRunner()
runner.load(MyGameScene())
runner.advance_frames(10)
assert runner.find("Player").position == Vec2(100, 200)
# Simulate input
runner.simulate_key_press("space")
runner.advance_frames(1)
assert runner.find("Player").velocity.y < 0 # Jumped
# Compare scene states
diff = scene_diff(before_state, after_state)
assert "Player.position" in diff.changed
Module Contents¶
Classes¶
Headless scene runner for testing game logic without rendering. |
|
Simulate input events for headless testing. |
|
Count nodes by type in a scene tree. Useful for debugging and assertions. |
|
Measure frame timing for performance testing. |
|
A single recorded input event. |
|
Records user input events and generates replayable test scripts. |
Functions¶
Compare two scene snapshots and return a list of human-readable differences. |
|
Return a tree-formatted text description of the scene hierarchy. |
|
Return a tree-formatted text description of a UI widget hierarchy. |
Data¶
API¶
- simvx.core.testing.__all__¶
[‘SceneRunner’, ‘InputSimulator’, ‘scene_diff’, ‘NodeCounter’, ‘FrameTimer’, ‘scene_describe’, ‘ui_d…
- class simvx.core.testing.SceneRunner(screen_size: tuple[float, float] = (800, 600))¶
Headless scene runner for testing game logic without rendering.
Manages a SceneTree, advances frames, and provides query helpers.
Initialization
- load(root_node: simvx.core.engine.Node) simvx.core.testing.SceneRunner¶
Load a scene by setting the root node.
- advance_frames(count: int = 1, dt: float | None = None, draw: bool = False) simvx.core.testing.SceneRunner¶
Advance the scene by N frames (process + physics_process each).
If draw is True, also calls
tree.draw()with a mock renderer each frame. This exercises draw() methods and catches errors that would otherwise only surface with a real Vulkan renderer.
- advance_time(seconds: float, dt: float | None = None) simvx.core.testing.SceneRunner¶
Advance the scene by approximately N seconds worth of frames.
- find(name_or_type, recursive: bool = True) simvx.core.engine.Node | None¶
Find a node by name (str) or by type in the scene tree.
- find_all(node_type: type) list[simvx.core.engine.Node]¶
Find all nodes of a given type in the scene.
- property root: simvx.core.engine.Node | None¶
- property frame_count: int¶
- property elapsed_time: float¶
- snapshot() dict¶
Capture current scene state as a serializable dict.
- class simvx.core.testing.InputSimulator¶
Simulate input events for headless testing.
Works by directly manipulating the Input singleton state, the same mechanism that platform adapters (GLFW, SDL3) use.
Usage: from simvx.core.input import Key sim = InputSimulator() sim.press_key(Key.SPACE) runner.advance_frames(1) sim.release_key(Key.SPACE)
- press_key(key) None¶
Simulate a key press. Accepts Key enum or int.
- release_key(key) None¶
Simulate a key release.
- tap_key(key) None¶
Press and release a key in one call (instant).
- press_mouse(button: int = 1, position: tuple[float, float] | None = None) None¶
Simulate mouse button press, optionally at a position.
- release_mouse(button: int = 1) None¶
Simulate mouse button release.
- click(position: tuple[float, float], button: int = 1) None¶
Click at a screen position (press + release).
- move_mouse(x: float, y: float) None¶
Move the mouse cursor to (x, y).
- scroll(dx: float = 0.0, dy: float = -1.0) None¶
Simulate scroll wheel. dy < 0 = scroll down, dy > 0 = scroll up.
- reset() None¶
Reset all input state to defaults.
- simvx.core.testing.scene_diff(before: dict, after: dict, _path: str = '') list[str]¶
Compare two scene snapshots and return a list of human-readable differences.
Each entry describes a single change, e.g.: “Root/Player.position: (0, 0) -> (10, 5)” “Root/Enemy: REMOVED” “Root/PowerUp: ADDED (Node2D)”
- class simvx.core.testing.NodeCounter¶
Count nodes by type in a scene tree. Useful for debugging and assertions.
- static count(root: simvx.core.engine.Node) dict[str, int]¶
Return a dict mapping type name to count.
- static total(root: simvx.core.engine.Node) int¶
Return total number of nodes in the tree.
- class simvx.core.testing.FrameTimer¶
Measure frame timing for performance testing.
Usage: timer = FrameTimer() for _ in range(100): timer.begin_frame() runner.advance_frames(1) timer.end_frame() print(f”Average: {timer.average_ms:.2f}ms, FPS: {timer.fps:.0f}”)
Initialization
- begin_frame() None¶
- end_frame() None¶
- property average_ms: float¶
- property max_ms: float¶
- property min_ms: float¶
- property fps: float¶
- property frame_count: int¶
- reset() None¶
- simvx.core.testing.scene_describe(root: simvx.core.engine.Node, include_properties: bool = True, include_layout: bool = True) str¶
Return a tree-formatted text description of the scene hierarchy.
Uses box-drawing chars for hierarchy. Each line shows the node name, type, and relevant state (position, rotation, scale, visibility, Property values). Designed for LLM consumption — an LLM can read this to understand what’s on screen.
- simvx.core.testing.ui_describe(root, include_layout: bool = True) str¶
Return a tree-formatted text description of a UI widget hierarchy.
Focused on UI-specific info: widget type, text content, rect, focus, visibility. Designed for LLM consumption — an LLM can read this to understand the UI state.
- class simvx.core.testing.RecordedEvent¶
A single recorded input event.
- timestamp: float¶
None
- event_type: str¶
None
- x: float¶
0.0
- y: float¶
0.0
- button: int¶
0
- key: str = <Multiline-String>¶
- char: str = <Multiline-String>¶
- target_path: str = <Multiline-String>¶
- class simvx.core.testing.TestRecorder(format: str = 'harness', **kwargs)¶
Bases:
simvx.core.engine.NodeRecords user input events and generates replayable test scripts.
Attach to a scene to record interactions. Call stop_recording() to get Python source code that replays the session using UITestHarness, DemoRunner steps, or raw InputSimulator API.
Toggle with F9 or programmatically via start/stop_recording().
Initialization
- format: str¶
‘harness’
- start_recording() None¶
Start capturing input events.
- stop_recording() str¶
Stop recording and return generated test code in the current format.
- save_recording(path: pathlib.Path, format: str | None = None) None¶
Save recorded script to a file.
- property is_recording: bool¶
- property event_count: int¶
- record_event(event: simvx.core.testing.RecordedEvent) None¶
Manually inject a recorded event (for testing the recorder itself).
- process(dt: float) None¶
- script_error_raised¶
‘Signal(…)’
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property process_mode: simvx.core.descriptors.ProcessMode¶
- reset_error() None¶
- add_child(node: simvx.core.node.Node) simvx.core.node.Node¶
- remove_child(node: simvx.core.node.Node)¶
- reparent(new_parent: simvx.core.node.Node)¶
- get_node(path: str) simvx.core.node.Node¶
- find_child(name: str, recursive: bool = False) simvx.core.node.Node | None¶
- find(node_type: type) simvx.core.node.Node | None¶
- property path: str¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- ready() None¶
- enter_tree() None¶
- exit_tree() None¶
- physics_process(dt: float) None¶
- draw(renderer) None¶
- input_event(event: simvx.core.events.InputEvent) None¶
- input(event: simvx.core.events.TreeInputEvent) None¶
- unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- destroy()¶
- queue_free¶
None
- property tree: simvx.core.scene_tree.SceneTree¶
- get_tree() simvx.core.scene_tree.SceneTree¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- get_settings¶
None
- __repr__()¶