summaryrefslogtreecommitdiff
path: root/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd
diff options
context:
space:
mode:
Diffstat (limited to 'addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd')
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd177
1 files changed, 177 insertions, 0 deletions
diff --git a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd
new file mode 100644
index 0000000..c38acf0
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd
@@ -0,0 +1,177 @@
+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"