Files
settled-reach/client/addons/gdUnit4/test/core/GdUnitSceneRunnerTest.gd
T
jpmschweitzerandClaude Opus 4.6 61e0762a84 chore(client): add gdUnit4 test framework addon
Vendor gdUnit4 for Godot 4 client testing (D-030).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 16:52:00 +01:00

487 lines
19 KiB
GDScript

# GdUnit generated TestSuite
@warning_ignore_start("redundant_await", "unsafe_method_access")
class_name GdUnitSceneRunnerTest
extends GdUnitTestSuite
# TestSuite generated from
const __source = 'res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd'
# loads the test runner and register for auto freeing after test
func load_test_scene() -> Node:
@warning_ignore("unsafe_method_access")
return auto_free(load("res://addons/gdUnit4/test/mocker/resources/scenes/TestScene.tscn").instantiate())
func before() -> void:
# use a dedicated FPS because we calculate frames by time
Engine.set_max_fps(60)
func after() -> void:
Engine.set_max_fps(0)
func test_get_property() -> void:
var runner := scene_runner(load_test_scene())
assert_that(runner.get_property("_box1")).is_instanceof(ColorRect)
assert_that(runner.get_property("_invalid")).is_equal("The property '_invalid' not exist checked loaded scene.")
assert_that(runner.get_property("_nullable")).is_null()
func test_set_property() -> void:
var runner := scene_runner(load_test_scene())
assert_that(runner.set_property("_invalid", 42)).is_equal(false)
assert_that(runner.set_property("_nullable", RefCounted.new())).is_equal(true)
assert_that(runner.get_property("_nullable")).is_instanceof(RefCounted)
func test_invoke_method() -> void:
var runner := scene_runner(load_test_scene())
assert_that(runner.invoke("add", 10, 12)).is_equal(22)
assert_that(runner.invoke("sub", 10, 12)).is_equal("The method 'sub' not exist checked loaded scene.")
@warning_ignore("unused_parameter")
func test_simulate_frames(timeout := 5000) -> void:
var runner := scene_runner(load_test_scene())
var box1 :ColorRect = runner.get_property("_box1")
# initial is white
assert_object(box1.color).is_equal(Color.WHITE)
# start color cycle by invoke the function 'start_color_cycle'
runner.invoke("start_color_cycle")
# we wait for 10 frames
await runner.simulate_frames(10)
# after 10 frame is still white
assert_object(box1.color).is_equal(Color.WHITE)
# we wait 30 more frames
await runner.simulate_frames(30)
# after 40 frames the box one should be changed to red
assert_object(box1.color).is_equal(Color.RED)
@warning_ignore("unused_parameter")
func test_simulate_frames_withdelay(timeout := 4000) -> void:
var runner := scene_runner(load_test_scene())
var box1 :ColorRect = runner.get_property("_box1")
# initial is white
assert_object(box1.color).is_equal(Color.WHITE)
# start color cycle by invoke the function 'start_color_cycle'
runner.invoke("start_color_cycle")
# we wait for 10 frames each with a 50ms delay
await runner.simulate_frames(10, 50)
# after 10 frame and in sum 500ms is should be changed to red
assert_object(box1.color).is_equal(Color.RED)
@warning_ignore("unused_parameter")
func test_run_scene_colorcycle(timeout := 2000) -> void:
var runner := scene_runner(load_test_scene())
var box1 :ColorRect = runner.get_property("_box1")
# verify inital color
assert_object(box1.color).is_equal(Color.WHITE)
# start color cycle by invoke the function 'start_color_cycle'
runner.invoke("start_color_cycle")
# await for each color cycle is emited
await runner.await_signal("panel_color_change", [box1, Color.RED])
assert_object(box1.color).is_equal(Color.RED)
await runner.await_signal("panel_color_change", [box1, Color.BLUE])
assert_object(box1.color).is_equal(Color.BLUE)
await runner.await_signal("panel_color_change", [box1, Color.GREEN])
assert_object(box1.color).is_equal(Color.GREEN)
func test_simulate_scene_inteaction_by_press_enter(timeout := 2000) -> void:
var runner := scene_runner(load_test_scene())
# inital no spell is fired
assert_object(runner.find_child("Spell")).is_null()
# fire spell be pressing enter key
runner.simulate_key_pressed(KEY_ENTER)
# wait until next frame
await await_idle_frame()
# verify a spell is created
assert_object(runner.find_child("Spell")).is_not_null()
# wait until spell is explode after around 1s
var spell := runner.find_child("Spell")
if spell == null:
return
await await_signal_on(spell, "spell_explode", [spell], timeout)
# verify spell is removed when is explode
assert_object(runner.find_child("Spell")).is_null()
# mock on a runner and spy on created spell
func test_simulate_scene_inteaction_in_combination_with_spy() -> void:
var spy_: Object = spy(load_test_scene())
# create a runner runner
var runner := scene_runner(spy_)
# simulate a key event to fire a spell
await runner.simulate_key_pressed(KEY_ENTER)
verify(spy_).create_spell()
var spell := runner.find_child("Spell")
assert_that(spell).is_not_null()
assert_that(spell.is_connected("spell_explode", Callable(spy_, "_destroy_spell"))).is_true()
func test_simulate_scene_interact_with_buttons() -> void:
var spyed_scene :Variant = spy("res://addons/gdUnit4/test/mocker/resources/scenes/TestScene.tscn")
var runner := scene_runner(spyed_scene)
# test button 1 interaction
await await_millis(1000)
runner.set_mouse_position(Vector2(60, 20))
runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
await await_idle_frame()
verify(spyed_scene)._on_panel_color_changed(spyed_scene._box1, Color.RED)
verify(spyed_scene)._on_panel_color_changed(spyed_scene._box1, Color.GRAY)
verify(spyed_scene, 0)._on_panel_color_changed(spyed_scene._box2, any_color())
verify(spyed_scene, 0)._on_panel_color_changed(spyed_scene._box3, any_color())
# test button 2 interaction
reset(spyed_scene)
await await_millis(1000)
runner.set_mouse_position(Vector2(160, 20))
runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
await await_idle_frame()
verify(spyed_scene, 0)._on_panel_color_changed(spyed_scene._box1, any_color())
verify(spyed_scene)._on_panel_color_changed(spyed_scene._box2, Color.RED)
verify(spyed_scene)._on_panel_color_changed(spyed_scene._box2, Color.GRAY)
verify(spyed_scene, 0)._on_panel_color_changed(spyed_scene._box3, any_color())
# test button 3 interaction (is changed after 1s to gray)
reset(spyed_scene)
await await_millis(1000)
runner.set_mouse_position(Vector2(260, 20))
runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
await await_idle_frame()
verify(spyed_scene, 0)._on_panel_color_changed(spyed_scene._box1, any_color())
verify(spyed_scene, 0)._on_panel_color_changed(spyed_scene._box2, any_color())
# is changed to red
verify(spyed_scene)._on_panel_color_changed(spyed_scene._box3, Color.RED)
# no gray
verify(spyed_scene, 0)._on_panel_color_changed(spyed_scene._box3, Color.GRAY)
# after one second is changed to gray
await await_millis(1200)
verify(spyed_scene)._on_panel_color_changed(spyed_scene._box3, Color.GRAY)
func test_await_func_without_time_factor() -> void:
var runner := scene_runner(load_test_scene())
await runner.await_func("color_cycle").is_equal("black")
func test_await_func_with_time_factor() -> void:
var runner := scene_runner(load_test_scene())
# set max time factor to minimize waiting time checked `runner.wait_func`
runner.set_time_factor(10)
await runner.await_func("color_cycle").wait_until(200).is_equal("black")
func test_await_signal_without_time_factor() -> void:
var runner := scene_runner(load_test_scene())
var box1 :ColorRect = runner.get_property("_box1")
runner.invoke("start_color_cycle")
await runner.await_signal("panel_color_change", [box1, Color.RED])
await runner.await_signal("panel_color_change", [box1, Color.BLUE])
await runner.await_signal("panel_color_change", [box1, Color.GREEN])
(
# should be interrupted is will never change to Color.KHAKI
await assert_failure_await(func x() -> void: await runner.await_signal( "panel_color_change", [box1, Color.KHAKI], 300))
).has_message("await_signal_on(panel_color_change, [%s, %s]) timed out after 300ms" % [str(box1), str(Color.KHAKI)])\
.has_line(207)
func test_await_signal_with_time_factor() -> void:
var runner := scene_runner(load_test_scene())
var box1 :ColorRect = runner.get_property("_box1")
# set max time factor to minimize waiting time checked `runner.wait_func`
runner.set_time_factor(10)
runner.invoke("start_color_cycle")
await runner.await_signal("panel_color_change", [box1, Color.RED], 100)
await runner.await_signal("panel_color_change", [box1, Color.BLUE], 100)
await runner.await_signal("panel_color_change", [box1, Color.GREEN], 100)
(
# should be interrupted is will never change to Color.KHAKI
await assert_failure_await(func x() -> void: await runner.await_signal("panel_color_change", [box1, Color.KHAKI], 30))
).has_message("await_signal_on(panel_color_change, [%s, %s]) timed out after 30ms" % [str(box1), str(Color.KHAKI)])\
.has_line(224)
func test_simulate_until_signal() -> void:
var runner := scene_runner(load_test_scene())
var box1 :ColorRect = runner.get_property("_box1")
# set max time factor to minimize waiting time checked `runner.wait_func`
runner.invoke("start_color_cycle")
await runner.simulate_until_signal("panel_color_change", box1, Color.RED)
await runner.simulate_until_signal("panel_color_change", box1, Color.BLUE)
await runner.simulate_until_signal("panel_color_change", box1, Color.GREEN)
@warning_ignore("unused_parameter")
func test_simulate_until_object_signal(timeout := 2000) -> void:
var runner := scene_runner(load_test_scene())
# inital no spell is fired
assert_object(runner.find_child("Spell")).is_null()
# fire spell be pressing enter key
runner.simulate_key_pressed(KEY_ENTER)
# wait until next frame
await await_idle_frame()
var spell := runner.find_child("Spell")
# simmulate scene until the spell is explode
await runner.simulate_until_object_signal(spell, "spell_explode", spell)
# verify spell is removed when is explode
assert_object(runner.find_child("Spell")).is_null()
func test_runner_by_null_instance() -> void:
var runner :GdUnitSceneRunnerImpl = scene_runner(null)
assert_object(runner._current_scene).is_null()
func test_runner_by_invalid_resource_path() -> void:
# not existing scene
@warning_ignore("unsafe_property_access")
assert_object(scene_runner("res://test_scene.tscn")._current_scene).is_null()
# not a path to a scene
@warning_ignore("unsafe_property_access")
assert_object(scene_runner("res://addons/gdUnit4/test/core/resources/scenes/simple_scene.gd")._current_scene).is_null()
func test_runner_by_invalid_uid_path() -> void:
# not existing scene
@warning_ignore("unsafe_property_access")
assert_object(scene_runner("uid://invalid_uid")._current_scene).is_null()
func test_runner_by_uid_path() -> void:
# uid is for res://addons/gdUnit4/test/core/resources/scenes/simple_scene.tscn
var runner := scene_runner("uid://cn8ucy2rheu0f")
assert_object(runner.scene()).is_instanceof(Node2D)
# verify the scene is freed when the runner is freed
var scene := runner.scene()
assert_bool(is_instance_valid(scene)).is_true()
runner._notification(NOTIFICATION_PREDELETE)
# give engine time to free the resources
await await_idle_frame()
# verify runner and scene is freed
assert_bool(is_instance_valid(scene)).is_false()
func test_runner_by_binary_resource_path() -> void:
var runner := scene_runner("res://addons/gdUnit4/test/core/resources/scenes/simple_scene.scn")
assert_object(runner.scene()).is_instanceof(Node2D)
# verify the scene is freed when the runner is freed
var scene := runner.scene()
assert_bool(is_instance_valid(scene)).is_true()
runner._notification(NOTIFICATION_PREDELETE)
# give engine time to free the resources
await await_idle_frame()
# verify runner and scene is freed
assert_bool(is_instance_valid(scene)).is_false()
func test_runner_by_resource_path() -> void:
var runner := scene_runner("res://addons/gdUnit4/test/core/resources/scenes/simple_scene.tscn")
assert_object(runner.scene()).is_instanceof(Node2D)
# verify the scene is freed when the runner is freed
var scene := runner.scene()
assert_bool(is_instance_valid(scene)).is_true()
runner._notification(NOTIFICATION_PREDELETE)
# give engine time to free the resources
await await_idle_frame()
# verify runner and scene is freed
assert_bool(is_instance_valid(scene)).is_false()
func test_runner_by_invalid_scene_instance() -> void:
var scene := RefCounted.new()
var runner: GdUnitSceneRunnerImpl = scene_runner(scene)
assert_object(runner._current_scene).is_null()
func test_runner_by_scene_instance() -> void:
var scene :Node = load("res://addons/gdUnit4/test/core/resources/scenes/simple_scene.tscn").instantiate()
var runner := scene_runner(scene)
assert_object(runner.scene()).is_instanceof(Node2D)
# verify the scene is freed when the runner is freed
runner._notification(NOTIFICATION_PREDELETE)
# give engine time to free the resources
await await_idle_frame()
# scene runner using external scene do not free the scene at exit
assert_bool(is_instance_valid(scene)).is_true()
# needs to be manually freed
scene.free()
func test_mouse_drag_and_drop() -> void:
var spy_scene :Variant = spy("res://addons/gdUnit4/test/core/resources/scenes/drag_and_drop/DragAndDropTestScene.tscn")
var runner := scene_runner(spy_scene)
var slot_left :TextureRect = $"/root/DragAndDropScene/left/TextureRect"
var slot_right :TextureRect = $"/root/DragAndDropScene/right/TextureRect"
var save_mouse_pos := get_tree().root.get_mouse_position()
# set inital mouse pos over the left slot
var mouse_pos := slot_left.global_position + Vector2(50, 50)
runner.set_mouse_position(mouse_pos)
#await await_millis(1000)
await await_idle_frame()
var event := InputEventMouseMotion.new()
event.position = mouse_pos
event.global_position = save_mouse_pos
verify(spy_scene, 1)._gui_input(event)
runner.simulate_mouse_button_press(MOUSE_BUTTON_LEFT)
await await_idle_frame()
assert_bool(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)).is_true()
# start drag&drop to left pannel
for i in 20:
runner.simulate_mouse_move(mouse_pos + Vector2(i*.4*i, 0))
await await_millis(40)
runner.simulate_mouse_button_release(MOUSE_BUTTON_LEFT)
await await_idle_frame()
assert_that(slot_right.texture).is_equal(slot_left.texture)
func test_touch_drag_and_drop_relative() -> void:
var spy_scene :Variant = spy("res://addons/gdUnit4/test/core/resources/scenes/drag_and_drop/DragAndDropTestScene.tscn")
var runner := scene_runner(spy_scene)
var slot_left :TextureRect = $"/root/DragAndDropScene/left/TextureRect"
var slot_right :TextureRect = $"/root/DragAndDropScene/right/TextureRect"
var drag_start := slot_left.global_position + Vector2(50, 50)
# set inital mouse pos over the left touch button
runner.simulate_screen_touch_press(0, drag_start)
await await_idle_frame()
assert_bool(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)).is_true()
assert_that(get_tree().root.get_mouse_position()).is_equal(drag_start)
# enable only for a small wait to for manual testing
#await await_millis(1000)
# start drag&drop to right touch button
var drag_end := drag_start + Vector2(140, 0)
await runner.simulate_screen_touch_drag_relative(0, Vector2(140, 0))
# verify
assert_that(slot_right.texture).is_equal(slot_left.texture)
assert_that(get_tree().root.get_mouse_position()).is_equal(drag_end)
assert_bool(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)).is_false()
func test_touch_drag_and_drop_absolute() -> void:
var spy_scene :Variant = spy("res://addons/gdUnit4/test/core/resources/scenes/drag_and_drop/DragAndDropTestScene.tscn")
var runner := scene_runner(spy_scene)
var slot_left :TextureRect = $"/root/DragAndDropScene/left/TextureRect"
var slot_right :TextureRect = $"/root/DragAndDropScene/right/TextureRect"
var drag_start := slot_left.global_position + Vector2(50, 50)
# set inital mouse pos over the left touch button
runner.simulate_screen_touch_press(0, drag_start)
await await_idle_frame()
assert_bool(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)).is_true()
assert_that(get_tree().root.get_mouse_position()).is_equal(drag_start)
# enable only for a small wait to for manual testing
#await await_millis(1000)
# start drag&drop to right touch button
var drag_end := Vector2(drag_start.x+140, drag_start.y)
await runner.simulate_screen_touch_drag_absolute(0, drag_end)
# verify
assert_that(slot_right.texture).is_equal(slot_left.texture)
assert_that(get_tree().root.get_mouse_position()).is_equal(drag_end)
assert_bool(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)).is_false()
func test_touch_drag_and_drop() -> void:
var spy_scene :Variant = spy("res://addons/gdUnit4/test/core/resources/scenes/drag_and_drop/DragAndDropTestScene.tscn")
var runner := scene_runner(spy_scene)
var slot_left :TextureRect = $"/root/DragAndDropScene/left/TextureRect"
var slot_right :TextureRect = $"/root/DragAndDropScene/right/TextureRect"
var drag_start := slot_left.global_position + Vector2(50, 50)
# start drag&drop to right touch button
var drag_end := Vector2(drag_start.x+140, drag_start.y)
await runner.simulate_screen_touch_drag_drop(0, drag_start, drag_end)
# verify
assert_that(slot_right.texture).is_equal(slot_left.texture)
assert_that(get_tree().root.get_mouse_position()).is_equal(drag_end)
assert_bool(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)).is_false()
func test_runner_GD_356() -> void:
# to avoid reporting the expected push_error as test failure we disable it
ProjectSettings.set_setting(GdUnitSettings.REPORT_PUSH_ERRORS, false)
var runner := scene_runner("res://addons/gdUnit4/test/core/resources/scenes/simple_scene.tscn")
var player: Object = runner.invoke("find_child", "Player", true, false)
assert_that(player).is_not_null()
await assert_func(player, "is_on_floor").wait_until(500).is_true()
assert_that(runner.scene()).is_not_null()
# run simulate_mouse_move_relative without await to reproduce https://github.com/MikeSchulze/gdUnit4/issues/356
# this results into releasing the scene while `simulate_mouse_move_relative` is processing the mouse move
runner.simulate_mouse_move_relative(Vector2(100, 100), 1.0)
assert_that(runner.scene()).is_not_null()
func test_move_window_to_foreground() -> void:
var runner := scene_runner("res://addons/gdUnit4/test/core/resources/scenes/simple_scene.tscn")
# we set inital to background
runner.move_window_to_background()
await runner.simulate_frames(1)
if not Engine.is_embedded_in_editor():
# We need a workaround because of https://github.com/godotengine/godot/issues/111119
var expecetd_mode := DisplayServer.WINDOW_MODE_WINDOWED if OS.get_name() == "Linux" else DisplayServer.WINDOW_MODE_MINIMIZED
assert_that(DisplayServer.window_get_mode()).is_equal(expecetd_mode)
else:
assert_that(DisplayServer.window_get_mode()).is_equal(DisplayServer.WINDOW_MODE_WINDOWED)
# force window to foreground
runner.move_window_to_foreground()
await runner.simulate_frames(1)
assert_that(DisplayServer.window_get_mode()).is_equal(DisplayServer.WINDOW_MODE_WINDOWED)
# https://github.com/MikeSchulze/gdUnit4/issues/1020
func test_load_scene_with_audio_player() -> void:
var runner := scene_runner("res://addons/gdUnit4/test/core/resources/scenes/scene_audio.tscn")
await runner.simulate_frames(1)
# we override the scene runner function for test purposes to hide push_error notifications
func scene_runner(scene :Variant, verbose := false) -> GdUnitSceneRunner:
return auto_free(GdUnitSceneRunnerImpl.new(scene, verbose, true))