Files
MovementTests/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd
Minimata 72bf3d4cc5
All checks were successful
Create tag and build when new code gets to main / Export (push) Successful in 7m6s
making sure the issue comes from GDUnit addon folder
2026-01-26 08:51:14 +01:00

178 lines
5.4 KiB
GDScript

extends GdUnitFuncAssert
const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd")
const DEFAULT_TIMEOUT := 2000
var _current_value_provider :ValueProvider
var _current_failure_message :String = ""
var _custom_failure_message :String = ""
var _additional_failure_message: String = ""
var _line_number := -1
var _timeout := DEFAULT_TIMEOUT
var _interrupted := false
var _sleep_timer :Timer = null
func _init(instance :Object, func_name :String, args := Array()) -> void:
_line_number = GdUnitAssertions.get_line_number()
GdAssertReports.reset_last_error_line_number()
# save the actual assert instance on the current thread context
GdUnitThreadManager.get_current_context().set_assert(self)
# verify at first the function name exists
if not instance.has_method(func_name):
@warning_ignore("return_value_discarded")
report_error("The function '%s' do not exists checked instance '%s'." % [func_name, instance])
_interrupted = true
else:
_current_value_provider = CallBackValueProvider.new(instance, func_name, args)
func _notification(what :int) -> void:
if what == NOTIFICATION_PREDELETE:
_interrupted = true
var main_node :Node = (Engine.get_main_loop() as SceneTree).root
if is_instance_valid(_current_value_provider):
_current_value_provider.dispose()
_current_value_provider = null
if is_instance_valid(_sleep_timer):
_sleep_timer.set_wait_time(0.0001)
_sleep_timer.stop()
main_node.remove_child(_sleep_timer)
_sleep_timer.free()
_sleep_timer = null
func report_success() -> GdUnitFuncAssert:
GdAssertReports.report_success()
return self
func report_error(failure :String) -> GdUnitFuncAssert:
_current_failure_message = GdAssertMessages.build_failure_message(failure, _additional_failure_message, _custom_failure_message)
GdAssertReports.report_error(_current_failure_message, _line_number)
return self
func failure_message() -> String:
return _current_failure_message
func override_failure_message(message: String) -> GdUnitFuncAssert:
_custom_failure_message = message
return self
func append_failure_message(message: String) -> GdUnitFuncAssert:
_additional_failure_message = message
return self
func wait_until(timeout := 2000) -> GdUnitFuncAssert:
if timeout <= 0:
push_warning("Invalid timeout param, alloed timeouts must be grater than 0. Use default timeout instead")
_timeout = DEFAULT_TIMEOUT
else:
_timeout = timeout
return self
func is_null() -> GdUnitFuncAssert:
await _validate_callback(cb_is_null)
return self
func is_not_null() -> GdUnitFuncAssert:
await _validate_callback(cb_is_not_null)
return self
func is_false() -> GdUnitFuncAssert:
await _validate_callback(cb_is_false)
return self
func is_true() -> GdUnitFuncAssert:
await _validate_callback(cb_is_true)
return self
func is_equal(expected: Variant) -> GdUnitFuncAssert:
await _validate_callback(cb_is_equal, expected)
return self
func is_not_equal(expected: Variant) -> GdUnitFuncAssert:
await _validate_callback(cb_is_not_equal, expected)
return self
# we need actually to define this Callable as functions otherwise we results into leaked scripts here
# this is actually a Godot bug and needs this kind of workaround
func cb_is_null(c :Variant, _e :Variant) -> bool: return c == null
func cb_is_not_null(c :Variant, _e :Variant) -> bool: return c != null
func cb_is_false(c :Variant, _e :Variant) -> bool: return c == false
func cb_is_true(c :Variant, _e :Variant) -> bool: return c == true
func cb_is_equal(c :Variant, e :Variant) -> bool: return GdObjects.equals(c,e)
func cb_is_not_equal(c :Variant, e :Variant) -> bool: return not GdObjects.equals(c, e)
func do_interrupt() -> void:
_interrupted = true
func _validate_callback(predicate :Callable, expected :Variant = null) -> void:
if _interrupted:
return
GdUnitMemoryObserver.guard_instance(self)
var time_scale := Engine.get_time_scale()
var timer := Timer.new()
timer.set_name("gdunit_funcassert_interrupt_timer_%d" % timer.get_instance_id())
var scene_tree := Engine.get_main_loop() as SceneTree
scene_tree.root.add_child(timer)
timer.add_to_group("GdUnitTimers")
@warning_ignore("return_value_discarded")
timer.timeout.connect(do_interrupt, CONNECT_DEFERRED)
timer.set_one_shot(true)
timer.start((_timeout/1000.0)*time_scale)
_sleep_timer = Timer.new()
_sleep_timer.set_name("gdunit_funcassert_sleep_timer_%d" % _sleep_timer.get_instance_id() )
scene_tree.root.add_child(_sleep_timer)
while true:
var current :Variant = await next_current_value()
# is interupted or predicate success
if _interrupted or predicate.call(current, expected):
break
if is_instance_valid(_sleep_timer):
_sleep_timer.start(0.05)
await _sleep_timer.timeout
_sleep_timer.stop()
await scene_tree.process_frame
if _interrupted:
# https://github.com/godotengine/godot/issues/73052
#var predicate_name = predicate.get_method()
var predicate_name :String = str(predicate).split('::')[1]
@warning_ignore("return_value_discarded")
report_error(GdAssertMessages.error_interrupted(
predicate_name.strip_edges().trim_prefix("cb_"),
expected,
LocalTime.elapsed(_timeout)
)
)
else:
@warning_ignore("return_value_discarded")
report_success()
_sleep_timer.free()
timer.free()
GdUnitMemoryObserver.unguard_instance(self)
func next_current_value() -> Variant:
@warning_ignore("redundant_await")
if is_instance_valid(_current_value_provider):
return await _current_value_provider.get_value()
return "invalid value"