summaryrefslogtreecommitdiff
path: root/addons/gdUnit4/src/asserts
diff options
context:
space:
mode:
Diffstat (limited to 'addons/gdUnit4/src/asserts')
-rw-r--r--addons/gdUnit4/src/asserts/CallBackValueProvider.gd25
-rw-r--r--addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/DefaultValueProvider.gd13
-rw-r--r--addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdAssertMessages.gd752
-rw-r--r--addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdAssertReports.gd54
-rw-r--r--addons/gdUnit4/src/asserts/GdAssertReports.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd424
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd80
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitAssertions.gd68
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd87
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd207
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd136
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd116
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd159
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd177
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd151
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd166
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd166
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd128
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd172
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd208
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd187
-rw-r--r--addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid1
-rw-r--r--addons/gdUnit4/src/asserts/ValueProvider.gd10
-rw-r--r--addons/gdUnit4/src/asserts/ValueProvider.gd.uid1
42 files changed, 3507 insertions, 0 deletions
diff --git a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd
new file mode 100644
index 0000000..6be4b3e
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd
@@ -0,0 +1,25 @@
+# a value provider unsing a callback to get `next` value from a certain function
+class_name CallBackValueProvider
+extends ValueProvider
+
+var _cb :Callable
+var _args :Array
+
+
+func _init(instance :Object, func_name :String, args :Array = Array(), force_error := true) -> void:
+ _cb = Callable(instance, func_name);
+ _args = args
+ if force_error and not _cb.is_valid():
+ push_error("Can't find function '%s' checked instance %s" % [func_name, instance])
+
+
+func get_value() -> Variant:
+ if not _cb.is_valid():
+ return null
+ if _args.is_empty():
+ return await _cb.call()
+ return await _cb.callv(_args)
+
+
+func dispose() -> void:
+ _cb = Callable()
diff --git a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid
new file mode 100644
index 0000000..20374e1
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid
@@ -0,0 +1 @@
+uid://da0u7lr425ojb
diff --git a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd
new file mode 100644
index 0000000..2f828fa
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd
@@ -0,0 +1,13 @@
+# default value provider, simple returns the initial value
+class_name DefaultValueProvider
+extends ValueProvider
+
+var _value: Variant
+
+
+func _init(value: Variant) -> void:
+ _value = value
+
+
+func get_value() -> Variant:
+ return _value
diff --git a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid
new file mode 100644
index 0000000..0759472
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid
@@ -0,0 +1 @@
+uid://d0oppd5oc256x
diff --git a/addons/gdUnit4/src/asserts/GdAssertMessages.gd b/addons/gdUnit4/src/asserts/GdAssertMessages.gd
new file mode 100644
index 0000000..5a2fa1c
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdAssertMessages.gd
@@ -0,0 +1,752 @@
+class_name GdAssertMessages
+extends Resource
+
+const WARN_COLOR = "#EFF883"
+const ERROR_COLOR = "#CD5C5C"
+const VALUE_COLOR = "#1E90FF"
+const SUB_COLOR := Color(1, 0, 0, .15)
+const ADD_COLOR := Color(0, 1, 0, .15)
+
+
+# Dictionary of control characters and their readable representations
+const CONTROL_CHARS = {
+ "\n": "<LF>", # Line Feed
+ "\r": "<CR>", # Carriage Return
+ "\t": "<TAB>", # Tab
+ "\b": "<BS>", # Backspace
+ "\f": "<FF>", # Form Feed
+ "\v": "<VT>", # Vertical Tab
+ "\a": "<BEL>", # Bell
+ "": "<ESC>" # Escape
+}
+
+
+static func format_dict(value :Variant) -> String:
+ if not value is Dictionary:
+ return str(value)
+
+ var dict_value: Dictionary = value
+ if dict_value.is_empty():
+ return "{ }"
+ var as_rows := var_to_str(value).split("\n")
+ for index in range( 1, as_rows.size()-1):
+ as_rows[index] = " " + as_rows[index]
+ as_rows[-1] = " " + as_rows[-1]
+ return "\n".join(as_rows)
+
+
+# improved version of InputEvent as text
+static func input_event_as_text(event :InputEvent) -> String:
+ var text := ""
+ if event is InputEventKey:
+ var key_event := event as InputEventKey
+ text += """
+ InputEventKey : keycode=%s (%s) pressed: %s
+ physical_keycode: %s
+ location: %s
+ echo: %s""" % [
+ key_event.keycode,
+ key_event.as_text_keycode(),
+ key_event.pressed,
+ key_event.physical_keycode,
+ key_event.location,
+ key_event.echo]
+ else:
+ text += event.as_text()
+ if event is InputEventMouse:
+ var mouse_event := event as InputEventMouse
+ text += """
+ global_position: %s""" % mouse_event.global_position
+ if event is InputEventWithModifiers:
+ var mouse_event := event as InputEventWithModifiers
+ text += """
+ --------
+ mods: %s
+ shift: %s
+ alt: %s
+ control: %s
+ meta: %s
+ command: %s""" % [
+ mouse_event.get_modifiers_mask(),
+ mouse_event.shift_pressed,
+ mouse_event.alt_pressed,
+ mouse_event.ctrl_pressed,
+ mouse_event.meta_pressed,
+ mouse_event.command_or_control_autoremap]
+ return text.dedent()
+
+
+static func _colored_string_div(characters: String) -> String:
+ return colored_array_div(characters.to_utf32_buffer().to_int32_array())
+
+
+static func colored_array_div(characters: PackedInt32Array) -> String:
+ if characters.is_empty():
+ return "<empty>"
+ var result := PackedInt32Array()
+ var index := 0
+ var missing_chars := PackedInt32Array()
+ var additional_chars := PackedInt32Array()
+
+ while index < characters.size():
+ var character := characters[index]
+ match character:
+ GdDiffTool.DIV_ADD:
+ index += 1
+ @warning_ignore("return_value_discarded")
+ additional_chars.append(characters[index])
+ GdDiffTool.DIV_SUB:
+ index += 1
+ @warning_ignore("return_value_discarded")
+ missing_chars.append(characters[index])
+ _:
+ if not missing_chars.is_empty():
+ result.append_array(format_chars(missing_chars, SUB_COLOR))
+ missing_chars = PackedInt32Array()
+ if not additional_chars.is_empty():
+ result.append_array(format_chars(additional_chars, ADD_COLOR))
+ additional_chars = PackedInt32Array()
+ @warning_ignore("return_value_discarded")
+ result.append(character)
+ index += 1
+
+ result.append_array(format_chars(missing_chars, SUB_COLOR))
+ result.append_array(format_chars(additional_chars, ADD_COLOR))
+ return result.to_byte_array().get_string_from_utf32()
+
+
+static func _typed_value(value :Variant) -> String:
+ return GdDefaultValueDecoder.decode(value)
+
+
+static func _warning(error :String) -> String:
+ return "[color=%s]%s[/color]" % [WARN_COLOR, error]
+
+
+static func _error(error :String) -> String:
+ return "[color=%s]%s[/color]" % [ERROR_COLOR, error]
+
+
+static func _nerror(number :Variant) -> String:
+ match typeof(number):
+ TYPE_INT:
+ return "[color=%s]%d[/color]" % [ERROR_COLOR, number]
+ TYPE_FLOAT:
+ return "[color=%s]%f[/color]" % [ERROR_COLOR, number]
+ _:
+ return "[color=%s]%s[/color]" % [ERROR_COLOR, str(number)]
+
+
+static func _colored(value: Variant, color: Color) -> String:
+ return "[color=%s]%s[/color]" % [color.to_html(), value]
+
+
+static func _colored_value(value :Variant) -> String:
+ match typeof(value):
+ TYPE_STRING, TYPE_STRING_NAME:
+ return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _colored_string_div(str(value))]
+ TYPE_INT:
+ return "'[color=%s]%d[/color]'" % [VALUE_COLOR, value]
+ TYPE_FLOAT:
+ return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _typed_value(value)]
+ TYPE_COLOR:
+ return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _typed_value(value)]
+ TYPE_OBJECT:
+ if value == null:
+ return "'[color=%s]<null>[/color]'" % [VALUE_COLOR]
+ if value is InputEvent:
+ var ie: InputEvent = value
+ return "[color=%s]<%s>[/color]" % [VALUE_COLOR, input_event_as_text(ie)]
+ var obj_value: Object = value
+ if obj_value.has_method("_to_string"):
+ return "[color=%s]<%s>[/color]" % [VALUE_COLOR, str(value)]
+ return "[color=%s]<%s>[/color]" % [VALUE_COLOR, obj_value.get_class()]
+ TYPE_DICTIONARY:
+ return "'[color=%s]%s[/color]'" % [VALUE_COLOR, format_dict(value)]
+ _:
+ if GdArrayTools.is_array_type(value):
+ return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _typed_value(value)]
+ return "'[color=%s]%s[/color]'" % [VALUE_COLOR, value]
+
+
+
+static func _index_report_as_table(index_reports :Array) -> String:
+ var table := "[table=3]$cells[/table]"
+ var header := "[cell][right][b]$text[/b][/right]\t[/cell]"
+ var cell := "[cell][right]$text[/right]\t[/cell]"
+ var cells := header.replace("$text", "Index") + header.replace("$text", "Current") + header.replace("$text", "Expected")
+ for report :Variant in index_reports:
+ var index :String = str(report["index"])
+ var current :String = str(report["current"])
+ var expected :String = str(report["expected"])
+ cells += cell.replace("$text", index) + cell.replace("$text", current) + cell.replace("$text", expected)
+ return table.replace("$cells", cells)
+
+
+
+static func orphan_warning(orphans_count: int) -> String:
+ return """
+ %s: Found %s possible orphan nodes.
+ Add %s to the end of the test to collect details.""".dedent().trim_prefix("\n") % [
+ _warning("WARNING:"),
+ _nerror(orphans_count),
+ _colored_value("collect_orphan_node_details()")
+ ]
+
+static func orphan_detected_on_suite_setup(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
+ return """
+ %s Detected %s orphan nodes!
+ [b]Verify your test suite setup.[/b]
+ %s""".dedent().trim_prefix("\n") % [
+ _warning("WARNING:"),
+ _nerror(orphans.size()),
+ _build_orphan_node_stacktrace(orphans)]
+
+
+static func orphan_detected_on_test_setup(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
+ return """
+ %s Detected %s orphan nodes on test setup!
+ [b]Check before_test() and after_test()![/b]
+ %s""".dedent().trim_prefix("\n") % [
+ _warning("WARNING:"),
+ _nerror(orphans.size()),
+ _build_orphan_node_stacktrace(orphans)
+ ]
+
+
+static func orphan_detected_on_test(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
+ return """
+ %s Detected %s orphan nodes!
+ %s""".dedent().trim_prefix("\n") % [
+ _warning("WARNING:"),
+ _nerror(orphans.size()),
+ _build_orphan_node_stacktrace(orphans)
+ ]
+
+
+static func _build_orphan_node_stacktrace(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
+ var stack_trace := "\n"
+ for orphan in orphans:
+ stack_trace += orphan.as_trace(orphan, true) + "\n"
+ return stack_trace.indent(" ")
+
+
+
+static func fuzzer_interuped(iterations: int, error: String) -> String:
+ return "%s %s %s\n %s" % [
+ _error("Found an error after"),
+ _colored_value(iterations + 1),
+ _error("test iterations"),
+ error]
+
+
+static func test_timeout(timeout :int) -> String:
+ return "%s\n %s" % [_error("Timeout !"), _colored_value("Test timed out after %s" % LocalTime.elapsed(timeout))]
+
+
+static func test_session_terminated() -> String:
+ return "%s" % _error("Test Session Terminated")
+
+
+# gdlint:disable = mixed-tabs-and-spaces
+static func test_suite_skipped(hint :String, skip_count :int) -> String:
+ return """
+ %s
+ Skipped %s tests
+ Reason: %s
+ """.dedent().trim_prefix("\n")\
+ % [_error("The Entire test-suite is skipped!"), _colored_value(skip_count), _colored_value(hint)]
+
+
+static func test_skipped(hint :String) -> String:
+ return """
+ %s
+ Reason: %s
+ """.dedent().trim_prefix("\n")\
+ % [_error("This test is skipped!"), _colored_value(hint)]
+
+
+static func error_not_implemented() -> String:
+ return _error("Test not implemented!")
+
+
+static func error_is_null(current :Variant) -> String:
+ return "%s %s but was %s" % [_error("Expecting:"), _colored_value(null), _colored_value(current)]
+
+
+static func error_is_not_null() -> String:
+ return "%s %s" % [_error("Expecting: not to be"), _colored_value(null)]
+
+
+static func error_equal(current :Variant, expected :Variant, index_reports :Array = []) -> String:
+ var report := """
+ %s
+ %s
+ but was
+ %s""".dedent().trim_prefix("\n") % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
+ if not index_reports.is_empty():
+ report += "\n\n%s\n%s" % [_error("Differences found:"), _index_report_as_table(index_reports)]
+ return report
+
+
+static func error_not_equal(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n not equal to\n %s" % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
+
+
+static func error_not_equal_case_insensetiv(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n not equal to (case insensitiv)\n %s" % [
+ _error("Expecting:"), _colored_value(expected), _colored_value(current)]
+
+
+static func error_is_empty(current :Variant) -> String:
+ return "%s\n must be empty but was\n %s" % [_error("Expecting:"), _colored_value(current)]
+
+
+static func error_is_not_empty() -> String:
+ return "%s\n must not be empty" % [_error("Expecting:")]
+
+
+static func error_is_same(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n to refer to the same object\n %s" % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
+
+
+@warning_ignore("unused_parameter")
+static func error_not_same(_current :Variant, expected :Variant) -> String:
+ return "%s\n %s" % [_error("Expecting not same:"), _colored_value(expected)]
+
+
+static func error_not_same_error(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n but was\n %s" % [_error("Expecting error message:"), _colored_value(expected), _colored_value(current)]
+
+
+static func error_is_instanceof(current: GdUnitResult, expected :GdUnitResult) -> String:
+ return "%s\n %s\n But it was %s" % [_error("Expected instance of:"),\
+ _colored_value(expected.or_else(null)), _colored_value(current.or_else(null))]
+
+
+# -- Boolean Assert specific messages -----------------------------------------------------
+static func error_is_true(current :Variant) -> String:
+ return "%s %s but is %s" % [_error("Expecting:"), _colored_value(true), _colored_value(current)]
+
+
+static func error_is_false(current :Variant) -> String:
+ return "%s %s but is %s" % [_error("Expecting:"), _colored_value(false), _colored_value(current)]
+
+
+# - Integer/Float Assert specific messages -----------------------------------------------------
+
+static func error_is_even(current :Variant) -> String:
+ return "%s\n %s must be even" % [_error("Expecting:"), _colored_value(current)]
+
+
+static func error_is_odd(current :Variant) -> String:
+ return "%s\n %s must be odd" % [_error("Expecting:"), _colored_value(current)]
+
+
+static func error_is_negative(current :Variant) -> String:
+ return "%s\n %s be negative" % [_error("Expecting:"), _colored_value(current)]
+
+
+static func error_is_not_negative(current :Variant) -> String:
+ return "%s\n %s be not negative" % [_error("Expecting:"), _colored_value(current)]
+
+
+static func error_is_zero(current :Variant) -> String:
+ return "%s\n equal to 0 but is %s" % [_error("Expecting:"), _colored_value(current)]
+
+
+static func error_is_not_zero() -> String:
+ return "%s\n not equal to 0" % [_error("Expecting:")]
+
+
+static func error_is_wrong_type(current_type :Variant.Type, expected_type :Variant.Type) -> String:
+ return "%s\n Expecting type %s but is %s" % [
+ _error("Unexpected type comparison:"),
+ _colored_value(GdObjects.type_as_string(current_type)),
+ _colored_value(GdObjects.type_as_string(expected_type))]
+
+
+static func error_is_value(operation :int, current :Variant, expected :Variant, expected2 :Variant = null) -> String:
+ match operation:
+ Comparator.EQUAL:
+ return "%s\n %s but was '%s'" % [_error("Expecting:"), _colored_value(expected), _nerror(current)]
+ Comparator.LESS_THAN:
+ return "%s\n %s but was '%s'" % [_error("Expecting to be less than:"), _colored_value(expected), _nerror(current)]
+ Comparator.LESS_EQUAL:
+ return "%s\n %s but was '%s'" % [_error("Expecting to be less than or equal:"), _colored_value(expected), _nerror(current)]
+ Comparator.GREATER_THAN:
+ return "%s\n %s but was '%s'" % [_error("Expecting to be greater than:"), _colored_value(expected), _nerror(current)]
+ Comparator.GREATER_EQUAL:
+ return "%s\n %s but was '%s'" % [_error("Expecting to be greater than or equal:"), _colored_value(expected), _nerror(current)]
+ Comparator.BETWEEN_EQUAL:
+ return "%s\n %s\n in range between\n %s <> %s" % [
+ _error("Expecting:"), _colored_value(current), _colored_value(expected), _colored_value(expected2)]
+ Comparator.NOT_BETWEEN_EQUAL:
+ return "%s\n %s\n not in range between\n %s <> %s" % [
+ _error("Expecting:"), _colored_value(current), _colored_value(expected), _colored_value(expected2)]
+ return "TODO create expected message"
+
+
+static func error_is_in(current :Variant, expected :Array) -> String:
+ return "%s\n %s\n is in\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(str(expected))]
+
+
+static func error_is_not_in(current :Variant, expected :Array) -> String:
+ return "%s\n %s\n is not in\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(str(expected))]
+
+
+# - StringAssert ---------------------------------------------------------------------------------
+static func error_equal_ignoring_case(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n but was\n %s (ignoring case)" % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
+
+
+static func error_contains(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n do contains\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
+
+
+static func error_not_contains(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n not do contain\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
+
+
+static func error_contains_ignoring_case(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n contains\n %s\n (ignoring case)" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
+
+
+static func error_not_contains_ignoring_case(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n not do contains\n %s\n (ignoring case)" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
+
+
+static func error_starts_with(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n to start with\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
+
+
+static func error_ends_with(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n to end with\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
+
+
+static func error_has_length(current :Variant, expected: int, compare_operator :int) -> String:
+ @warning_ignore("unsafe_method_access")
+ var current_length :Variant = current.length() if current != null else null
+ match compare_operator:
+ Comparator.EQUAL:
+ return "%s\n %s but was '%s' in\n %s" % [
+ _error("Expecting size:"), _colored_value(expected), _nerror(current_length), _colored_value(current)]
+ Comparator.LESS_THAN:
+ return "%s\n %s but was '%s' in\n %s" % [
+ _error("Expecting size to be less than:"), _colored_value(expected), _nerror(current_length), _colored_value(current)]
+ Comparator.LESS_EQUAL:
+ return "%s\n %s but was '%s' in\n %s" % [
+ _error("Expecting size to be less than or equal:"), _colored_value(expected),
+ _nerror(current_length), _colored_value(current)]
+ Comparator.GREATER_THAN:
+ return "%s\n %s but was '%s' in\n %s" % [
+ _error("Expecting size to be greater than:"), _colored_value(expected),
+ _nerror(current_length), _colored_value(current)]
+ Comparator.GREATER_EQUAL:
+ return "%s\n %s but was '%s' in\n %s" % [
+ _error("Expecting size to be greater than or equal:"), _colored_value(expected),
+ _nerror(current_length), _colored_value(current)]
+ return "TODO create expected message"
+
+
+# - ArrayAssert specific messgaes ---------------------------------------------------
+
+static func error_arr_contains(current: Variant, expected: Variant, not_expect: Variant, not_found: Variant, by_reference: bool) -> String:
+ var failure_message := "Expecting contains SAME elements:" if by_reference else "Expecting contains elements:"
+ var error := "%s\n %s\n do contains (in any order)\n %s" % [
+ _error(failure_message), _colored_value(current), _colored_value(expected)]
+ if not is_empty(not_expect):
+ error += "\nbut some elements where not expected:\n %s" % _colored_value(not_expect)
+ if not is_empty(not_found):
+ var prefix := "but" if is_empty(not_expect) else "and"
+ error += "\n%s could not find elements:\n %s" % [prefix, _colored_value(not_found)]
+ return error
+
+
+static func error_arr_contains_exactly(
+ current: Variant,
+ expected: Variant,
+ not_expect: Variant,
+ not_found: Variant, compare_mode: GdObjects.COMPARE_MODE) -> String:
+ var failure_message := (
+ "Expecting contains exactly elements:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
+ else "Expecting contains SAME exactly elements:"
+ )
+ if is_empty(not_expect) and is_empty(not_found):
+ var arr_current: Array = current
+ var arr_expected: Array = expected
+ var diff := _find_first_diff(arr_current, arr_expected)
+ return "%s\n %s\n do contains (in same order)\n %s\n but has different order %s" % [
+ _error(failure_message), _colored_value(current), _colored_value(expected), diff]
+
+ var error := "%s\n %s\n do contains (in same order)\n %s" % [
+ _error(failure_message), _colored_value(current), _colored_value(expected)]
+ if not is_empty(not_expect):
+ error += "\nbut some elements where not expected:\n %s" % _colored_value(not_expect)
+ if not is_empty(not_found):
+ var prefix := "but" if is_empty(not_expect) else "and"
+ error += "\n%s could not find elements:\n %s" % [prefix, _colored_value(not_found)]
+ return error
+
+
+static func error_arr_contains_exactly_in_any_order(
+ current: Variant,
+ expected: Variant,
+ not_expect: Variant,
+ not_found: Variant,
+ compare_mode: GdObjects.COMPARE_MODE) -> String:
+
+ var failure_message := (
+ "Expecting contains exactly elements:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
+ else "Expecting contains SAME exactly elements:"
+ )
+ var error := "%s\n %s\n do contains exactly (in any order)\n %s" % [
+ _error(failure_message), _colored_value(current), _colored_value(expected)]
+ if not is_empty(not_expect):
+ error += "\nbut some elements where not expected:\n %s" % _colored_value(not_expect)
+ if not is_empty(not_found):
+ var prefix := "but" if is_empty(not_expect) else "and"
+ error += "\n%s could not find elements:\n %s" % [prefix, _colored_value(not_found)]
+ return error
+
+
+static func error_arr_not_contains(current: Variant, expected: Variant, found: Variant, compare_mode: GdObjects.COMPARE_MODE) -> String:
+ var failure_message := "Expecting:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST else "Expecting SAME:"
+ var error := "%s\n %s\n do not contains\n %s" % [
+ _error(failure_message), _colored_value(current), _colored_value(expected)]
+ if not is_empty(found):
+ error += "\n but found elements:\n %s" % _colored_value(found)
+ return error
+
+
+# - DictionaryAssert specific messages ----------------------------------------------
+static func error_contains_keys(current :Array, expected :Array, keys_not_found :Array, compare_mode :GdObjects.COMPARE_MODE) -> String:
+ var failure := (
+ "Expecting contains keys:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
+ else "Expecting contains SAME keys:"
+ )
+ return "%s\n %s\n to contains:\n %s\n but can't find key's:\n %s" % [
+ _error(failure), _colored_value(current), _colored_value(expected), _colored_value(keys_not_found)]
+
+
+static func error_not_contains_keys(current :Array, expected :Array, keys_not_found :Array, compare_mode :GdObjects.COMPARE_MODE) -> String:
+ var failure := (
+ "Expecting NOT contains keys:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
+ else "Expecting NOT contains SAME keys"
+ )
+ return "%s\n %s\n do not contains:\n %s\n but contains key's:\n %s" % [
+ _error(failure), _colored_value(current), _colored_value(expected), _colored_value(keys_not_found)]
+
+
+static func error_contains_key_value(key :Variant, value :Variant, current_value :Variant, compare_mode :GdObjects.COMPARE_MODE) -> String:
+ var failure := (
+ "Expecting contains key and value:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
+ else "Expecting contains SAME key and value:"
+ )
+ return "%s\n %s : %s\n but contains\n %s : %s" % [
+ _error(failure), _colored_value(key), _colored_value(value), _colored_value(key), _colored_value(current_value)]
+
+
+# - ResultAssert specific errors ----------------------------------------------------
+static func error_result_is_empty(current :GdUnitResult) -> String:
+ return _result_error_message(current, GdUnitResult.EMPTY)
+
+
+static func error_result_is_success(current :GdUnitResult) -> String:
+ return _result_error_message(current, GdUnitResult.SUCCESS)
+
+
+static func error_result_is_warning(current :GdUnitResult) -> String:
+ return _result_error_message(current, GdUnitResult.WARN)
+
+
+static func error_result_is_error(current :GdUnitResult) -> String:
+ return _result_error_message(current, GdUnitResult.ERROR)
+
+
+static func error_result_has_message(current :String, expected :String) -> String:
+ return "%s\n %s\n but was\n %s." % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
+
+
+static func error_result_has_message_on_success(expected :String) -> String:
+ return "%s\n %s\n but the GdUnitResult is a success." % [_error("Expecting:"), _colored_value(expected)]
+
+
+static func error_result_is_value(current :Variant, expected :Variant) -> String:
+ return "%s\n %s\n but was\n %s." % [_error("Expecting to contain same value:"), _colored_value(expected), _colored_value(current)]
+
+
+static func _result_error_message(current :GdUnitResult, expected_type :int) -> String:
+ if current == null:
+ return _error("Expecting the result must be a %s but was <null>." % result_type(expected_type))
+ if current.is_success():
+ return _error("Expecting the result must be a %s but was SUCCESS." % result_type(expected_type))
+ var error := "Expecting the result must be a %s but was %s:" % [result_type(expected_type), result_type(current._state)]
+ return "%s\n %s" % [_error(error), _colored_value(result_message(current))]
+
+
+static func error_interrupted(func_name :String, expected :Variant, elapsed :String) -> String:
+ func_name = humanized(func_name)
+ if expected == null:
+ return "%s %s but timed out after %s" % [_error("Expected:"), func_name, elapsed]
+ return "%s %s %s but timed out after %s" % [_error("Expected:"), func_name, _colored_value(expected), elapsed]
+
+
+static func error_wait_signal(signal_name :String, args :Array, elapsed :String) -> String:
+ if args.is_empty():
+ return "%s %s but timed out after %s" % [
+ _error("Expecting emit signal:"), _colored_value(signal_name + "()"), elapsed]
+ return "%s %s but timed out after %s" % [
+ _error("Expecting emit signal:"), _colored_value(signal_name + "(" + str(args) + ")"), elapsed]
+
+
+static func error_signal_emitted(signal_name :String, args :Array, elapsed :String) -> String:
+ if args.is_empty():
+ return "%s %s but is emitted after %s" % [
+ _error("Expecting do not emit signal:"), _colored_value(signal_name + "()"), elapsed]
+ return "%s %s but is emitted after %s" % [
+ _error("Expecting do not emit signal:"), _colored_value(signal_name + "(" + str(args) + ")"), elapsed]
+
+
+static func error_await_signal_on_invalid_instance(source :Variant, signal_name :String, args :Array) -> String:
+ return "%s\n await_signal_on(%s, %s, %s)" % [
+ _error("Invalid source! Can't await on signal:"), _colored_value(source), signal_name, args]
+
+
+static func result_type(type :int) -> String:
+ match type:
+ GdUnitResult.SUCCESS: return "SUCCESS"
+ GdUnitResult.WARN: return "WARNING"
+ GdUnitResult.ERROR: return "ERROR"
+ GdUnitResult.EMPTY: return "EMPTY"
+ return "UNKNOWN"
+
+
+static func result_message(result :GdUnitResult) -> String:
+ match result._state:
+ GdUnitResult.SUCCESS: return ""
+ GdUnitResult.WARN: return result.warn_message()
+ GdUnitResult.ERROR: return result.error_message()
+ GdUnitResult.EMPTY: return ""
+ return "UNKNOWN"
+# -----------------------------------------------------------------------------------
+
+# - Spy|Mock specific errors ----------------------------------------------------
+static func error_no_more_interactions(summary :Dictionary) -> String:
+ var interactions := PackedStringArray()
+ for args :Array in summary.keys():
+ var times :int = summary[args]
+ @warning_ignore("return_value_discarded")
+ interactions.append(_format_arguments(args, times))
+ return "%s\n%s\n%s" % [_error("Expecting no more interactions!"), _error("But found interactions on:"), "\n".join(interactions)]
+
+
+static func error_validate_interactions(current_interactions: Dictionary, expected_interactions: Dictionary) -> String:
+ var collected_interactions := PackedStringArray()
+ for args: Array in current_interactions.keys():
+ var times: int = current_interactions[args]
+ @warning_ignore("return_value_discarded")
+ collected_interactions.append(_format_arguments(args, times))
+
+ var arguments: Array = expected_interactions.keys()[0]
+ var interactions: int = expected_interactions.values()[0]
+ var expected_interaction := _format_arguments(arguments, interactions)
+ return "%s\n%s\n%s\n%s" % [
+ _error("Expecting interaction on:"), expected_interaction, _error("But found interactions on:"), "\n".join(collected_interactions)]
+
+
+static func _format_arguments(args :Array, times :int) -> String:
+ var fname :String = args[0]
+ var fargs := args.slice(1) as Array
+ var typed_args := _to_typed_args(fargs)
+ var fsignature := _colored_value("%s(%s)" % [fname, ", ".join(typed_args)])
+ return " %s %d time's" % [fsignature, times]
+
+
+static func _to_typed_args(args :Array) -> PackedStringArray:
+ var typed := PackedStringArray()
+ for arg :Variant in args:
+ @warning_ignore("return_value_discarded")
+ typed.append(_format_arg(arg) + " :" + GdObjects.type_as_string(typeof(arg)))
+ return typed
+
+
+static func _format_arg(arg :Variant) -> String:
+ if arg is InputEvent:
+ var ie: InputEvent = arg
+ return input_event_as_text(ie)
+ return str(arg)
+
+
+static func _find_first_diff(left :Array, right :Array) -> String:
+ for index in left.size():
+ var l :Variant = left[index]
+ var r :Variant = "<no entry>" if index >= right.size() else right[index]
+ if not GdObjects.equals(l, r):
+ return "at position %s\n '%s' vs '%s'" % [_colored_value(index), _typed_value(l), _typed_value(r)]
+ return ""
+
+
+static func error_has_size(current :Variant, expected: int) -> String:
+ @warning_ignore("unsafe_method_access")
+ var current_size :Variant = null if current == null else current.size()
+ return "%s\n %s\n but was\n %s" % [_error("Expecting size:"), _colored_value(expected), _colored_value(current_size)]
+
+
+static func error_contains_exactly(current: Array, expected: Array) -> String:
+ return "%s\n %s\n but was\n %s" % [_error("Expecting exactly equal:"), _colored_value(expected), _colored_value(current)]
+
+
+static func format_chars(characters: PackedInt32Array, type: Color) -> PackedInt32Array:
+ if characters.size() == 0:# or characters[0] == 10:
+ return characters
+
+ # Replace each control character with its readable form
+ var formatted_text := characters.to_byte_array().get_string_from_utf32()
+ for control_char: String in CONTROL_CHARS:
+ var replace_text: String = CONTROL_CHARS[control_char]
+ formatted_text = formatted_text.replace(control_char, replace_text)
+
+ # Handle special ASCII control characters (0x00-0x1F, 0x7F)
+ var ascii_text := ""
+ for i in formatted_text.length():
+ var character := formatted_text[i]
+ var code := character.unicode_at(0)
+ if code < 0x20 and not CONTROL_CHARS.has(character): # Control characters not handled above
+ ascii_text += "<0x%02X>" % code
+ elif code == 0x7F: # DEL character
+ ascii_text += "<DEL>"
+ else:
+ ascii_text += character
+
+ var message := "[bgcolor=#%s][color=white]%s[/color][/bgcolor]" % [
+ type.to_html(),
+ ascii_text
+ ]
+
+ var result := PackedInt32Array()
+ result.append_array(message.to_utf32_buffer().to_int32_array())
+ return result
+
+
+static func format_invalid(value :String) -> String:
+ return "[bgcolor=#%s][color=with]%s[/color][/bgcolor]" % [SUB_COLOR.to_html(), value]
+
+
+static func humanized(value :String) -> String:
+ return value.replace("_", " ")
+
+
+static func build_failure_message(failure :String, additional_failure_message: String, custom_failure_message: String) -> String:
+ var message := failure if custom_failure_message.is_empty() else custom_failure_message
+ if additional_failure_message.is_empty():
+ return message
+ return """
+ %s
+ [color=LIME_GREEN][b]Additional info:[/b][/color]
+ %s""".dedent().trim_prefix("\n") % [message, additional_failure_message]
+
+
+static func is_empty(value: Variant) -> bool:
+ var arry_value: Array = value
+ return arry_value != null and arry_value.is_empty()
diff --git a/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid
new file mode 100644
index 0000000..f1336de
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid
@@ -0,0 +1 @@
+uid://cf4cfpmy5gb5s
diff --git a/addons/gdUnit4/src/asserts/GdAssertReports.gd b/addons/gdUnit4/src/asserts/GdAssertReports.gd
new file mode 100644
index 0000000..06b72ed
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdAssertReports.gd
@@ -0,0 +1,54 @@
+class_name GdAssertReports
+extends RefCounted
+
+const LAST_ERROR = "last_assert_error_message"
+const LAST_ERROR_LINE = "last_assert_error_line"
+
+
+static func report_success() -> void:
+ GdUnitSignals.instance().gdunit_set_test_failed.emit(false)
+ GdAssertReports.set_last_error_line_number(-1)
+ Engine.remove_meta(LAST_ERROR)
+
+
+static func report_warning(message :String, line_number :int) -> void:
+ GdUnitSignals.instance().gdunit_set_test_failed.emit(false)
+ send_report(GdUnitReport.new().create(GdUnitReport.WARN, line_number, message))
+
+
+static func report_error(message:String, line_number :int) -> void:
+ GdUnitSignals.instance().gdunit_set_test_failed.emit(true)
+ GdAssertReports.set_last_error_line_number(line_number)
+ Engine.set_meta(LAST_ERROR, message)
+ # if we expect to fail we handle as success test
+ if _do_expect_assert_failing():
+ return
+ send_report(GdUnitReport.new().create(GdUnitReport.FAILURE, line_number, message))
+
+
+static func reset_last_error_line_number() -> void:
+ Engine.remove_meta(LAST_ERROR_LINE)
+
+
+static func set_last_error_line_number(line_number :int) -> void:
+ Engine.set_meta(LAST_ERROR_LINE, line_number)
+
+
+static func get_last_error_line_number() -> int:
+ if Engine.has_meta(LAST_ERROR_LINE):
+ return Engine.get_meta(LAST_ERROR_LINE)
+ return -1
+
+
+static func _do_expect_assert_failing() -> bool:
+ if Engine.has_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES):
+ return Engine.get_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES)
+ return false
+
+
+static func current_failure() -> String:
+ return Engine.get_meta(LAST_ERROR)
+
+
+static func send_report(report :GdUnitReport) -> void:
+ GdUnitThreadManager.get_current_context().get_execution_context().add_report(report)
diff --git a/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid
new file mode 100644
index 0000000..10cae25
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid
@@ -0,0 +1 @@
+uid://cnmp765i0d7kk
diff --git a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd
new file mode 100644
index 0000000..63b45a0
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd
@@ -0,0 +1,424 @@
+class_name GdUnitArrayAssertImpl
+extends GdUnitArrayAssert
+
+
+var _base: GdUnitAssertImpl
+var _current_value_provider: ValueProvider
+var _type_check: bool
+
+
+func _init(current: Variant, type_check := true) -> void:
+ _type_check = type_check
+ _current_value_provider = DefaultValueProvider.new(current)
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not _validate_value_type(current):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitArrayAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event: int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func report_success() -> GdUnitArrayAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error: String) -> GdUnitArrayAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitArrayAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitArrayAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func _validate_value_type(value: Variant) -> bool:
+ return value == null or GdArrayTools.is_array_type(value)
+
+
+func get_current_value() -> Variant:
+ return _current_value_provider.get_value()
+
+
+func max_length(left: Variant, right: Variant) -> int:
+ var ls := str(left).length()
+ var rs := str(right).length()
+ return rs if ls < rs else ls
+
+
+# gdlint: disable=function-name
+func _toPackedStringArray(value: Variant) -> PackedStringArray:
+ if GdArrayTools.is_array_type(value):
+ @warning_ignore("unsafe_cast")
+ return PackedStringArray(value as Array)
+ return PackedStringArray([str(value)])
+
+
+func _array_equals_div(current: Variant, expected: Variant, case_sensitive: bool = false) -> Array:
+ var current_value := _toPackedStringArray(current)
+ var expected_value := _toPackedStringArray(expected)
+ var index_report := Array()
+ for index in current_value.size():
+ var c := current_value[index]
+ if index < expected_value.size():
+ var e := expected_value[index]
+ if not GdObjects.equals(c, e, case_sensitive):
+ var length := max_length(c, e)
+ current_value[index] = GdAssertMessages.format_invalid(c.lpad(length))
+ expected_value[index] = e.lpad(length)
+ index_report.push_back({"index": index, "current": c, "expected": e})
+ else:
+ current_value[index] = GdAssertMessages.format_invalid(c)
+ index_report.push_back({"index": index, "current": c, "expected": "<N/A>"})
+
+ for index in range(current_value.size(), expected_value.size()):
+ var value := expected_value[index]
+ expected_value[index] = GdAssertMessages.format_invalid(value)
+ index_report.push_back({"index": index, "current": "<N/A>", "expected": value})
+ return [current_value, expected_value, index_report]
+
+
+func _array_div(compare_mode: GdObjects.COMPARE_MODE, left: Array[Variant], right: Array[Variant], _same_order := false) -> Array[Variant]:
+ var not_expect := left.duplicate(true)
+ var not_found := right.duplicate(true)
+ for index_c in left.size():
+ var c: Variant = left[index_c]
+ for index_e in right.size():
+ var e: Variant = right[index_e]
+ if GdObjects.equals(c, e, false, compare_mode):
+ GdArrayTools.erase_value(not_expect, e)
+ GdArrayTools.erase_value(not_found, c)
+ break
+ return [not_expect, not_found]
+
+
+func _contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
+ var by_reference := compare_mode == GdObjects.COMPARE_MODE.OBJECT_REFERENCE
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant = _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+
+ if current_value == null:
+ return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], expected_value, by_reference))
+ @warning_ignore("unsafe_cast")
+ var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant])
+ #var not_expect := diffs[0] as Array
+ var not_found: Array = diffs[1]
+ if not not_found.is_empty():
+ return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], not_found, by_reference))
+ return report_success()
+
+
+func _contains_exactly(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant = _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+
+ if current_value == null:
+ return report_error(GdAssertMessages.error_arr_contains_exactly(null, expected_value, [], expected_value, compare_mode))
+ # has same content in same order
+ if _is_equal(current_value, expected_value, false, compare_mode):
+ return report_success()
+ # check has same elements but in different order
+ if _is_equals_sorted(current_value, expected_value, false, compare_mode):
+ return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, [], [], compare_mode))
+ # find the difference
+ @warning_ignore("unsafe_cast")
+ var diffs := _array_div(compare_mode,
+ current_value as Array[Variant],
+ expected_value as Array[Variant],
+ GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+ var not_expect: Array[Variant] = diffs[0]
+ var not_found: Array[Variant] = diffs[1]
+ return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, not_expect, not_found, compare_mode))
+
+
+func _contains_exactly_in_any_order(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant = _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+
+ if current_value == null:
+ return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [],
+ expected_value, compare_mode))
+ # find the difference
+ @warning_ignore("unsafe_cast")
+ var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant], false)
+ var not_expect: Array[Variant] = diffs[0]
+ var not_found: Array[Variant] = diffs[1]
+ if not_expect.is_empty() and not_found.is_empty():
+ return report_success()
+ return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, not_expect,
+ not_found, compare_mode))
+
+
+func _not_contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant = _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+ if current_value == null:
+ return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [],
+ expected_value, compare_mode))
+ @warning_ignore("unsafe_cast")
+ var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant])
+ var found: Array[Variant] = diffs[0]
+ @warning_ignore("unsafe_cast")
+ if found.size() == (current_value as Array).size():
+ return report_success()
+ @warning_ignore("unsafe_cast")
+ var diffs2 := _array_div(compare_mode, expected_value as Array[Variant], diffs[1] as Array[Variant])
+ return report_error(GdAssertMessages.error_arr_not_contains(current_value, expected_value, diffs2[0], compare_mode))
+
+
+func is_null() -> GdUnitArrayAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitArrayAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(...expected: Array) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant= _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+ if current_value == null and expected_value != null:
+ return report_error(GdAssertMessages.error_equal(null, expected_value))
+
+ if not _is_equal(current_value, expected_value):
+ var diff := _array_equals_div(current_value, expected_value)
+ var expected_as_list := GdArrayTools.as_string(diff[0], false)
+ var current_as_list := GdArrayTools.as_string(diff[1], false)
+ var index_report: Array = diff[2]
+ return report_error(GdAssertMessages.error_equal(expected_as_list, current_as_list, index_report))
+ return report_success()
+
+
+# Verifies that the current Array is equal to the given one, ignoring case considerations.
+func is_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant = _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+ if current_value == null and expected_value != null:
+ @warning_ignore("unsafe_cast")
+ return report_error(GdAssertMessages.error_equal(null, GdArrayTools.as_string(expected_value)))
+
+ if not _is_equal(current_value, expected_value, true):
+ @warning_ignore("unsafe_cast")
+ var diff := _array_equals_div(current_value, expected_value, true)
+ var expected_as_list := GdArrayTools.as_string(diff[0])
+ var current_as_list := GdArrayTools.as_string(diff[1])
+ var index_report: Array = diff[2]
+ return report_error(GdAssertMessages.error_equal(expected_as_list, current_as_list, index_report))
+ return report_success()
+
+
+func is_not_equal(...expected: Array) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant = _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+
+ if _is_equal(current_value, expected_value):
+ return report_error(GdAssertMessages.error_not_equal(current_value, expected_value))
+ return report_success()
+
+
+func is_not_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ var expected_value: Variant = _extract_variadic_value(expected)
+ if not _validate_value_type(expected_value):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
+
+ if _is_equal(current_value, expected_value, true):
+ @warning_ignore("unsafe_cast")
+ var c := GdArrayTools.as_string(current_value as Array)
+ @warning_ignore("unsafe_cast")
+ var e := GdArrayTools.as_string(expected_value)
+ return report_error(GdAssertMessages.error_not_equal_case_insensetiv(c, e))
+ return report_success()
+
+
+func is_empty() -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ @warning_ignore("unsafe_cast")
+ if current_value == null or (current_value as Array).size() > 0:
+ return report_error(GdAssertMessages.error_is_empty(current_value))
+ return report_success()
+
+
+func is_not_empty() -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ @warning_ignore("unsafe_cast")
+ if current_value != null and (current_value as Array).size() == 0:
+ return report_error(GdAssertMessages.error_is_not_empty())
+ return report_success()
+
+
+@warning_ignore("unused_parameter", "shadowed_global_identifier")
+func is_same(expected: Variant) -> GdUnitArrayAssert:
+ if not _validate_value_type(expected):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected))
+ var current: Variant = get_current_value()
+ if not is_same(current, expected):
+ @warning_ignore("return_value_discarded")
+ report_error(GdAssertMessages.error_is_same(current, expected))
+ return self
+
+
+func is_not_same(expected: Variant) -> GdUnitArrayAssert:
+ if not _validate_value_type(expected):
+ return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected))
+ var current: Variant = get_current_value()
+ if is_same(current, expected):
+ @warning_ignore("return_value_discarded")
+ report_error(GdAssertMessages.error_not_same(current, expected))
+ return self
+
+
+func has_size(expected: int) -> GdUnitArrayAssert:
+ var current_value: Variant = get_current_value()
+ @warning_ignore("unsafe_cast")
+ if current_value == null or (current_value as Array).size() != expected:
+ return report_error(GdAssertMessages.error_has_size(current_value, expected))
+ return report_success()
+
+
+func contains(...expected: Array) -> GdUnitArrayAssert:
+ return _contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+
+
+func contains_exactly(...expected: Array) -> GdUnitArrayAssert:
+ return _contains_exactly(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+
+
+func contains_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert:
+ return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+
+
+func contains_same(...expected: Array) -> GdUnitArrayAssert:
+ return _contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
+
+
+func contains_same_exactly(...expected: Array) -> GdUnitArrayAssert:
+ return _contains_exactly(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
+
+
+func contains_same_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert:
+ return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
+
+
+func not_contains(...expected: Array) -> GdUnitArrayAssert:
+ return _not_contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+
+
+func not_contains_same(...expected: Array) -> GdUnitArrayAssert:
+ return _not_contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
+
+
+func is_instanceof(expected: Variant) -> GdUnitAssert:
+ @warning_ignore("unsafe_method_access")
+ _base.is_instanceof(expected)
+ return self
+
+
+func extract(func_name: String, ...func_args: Array) -> GdUnitArrayAssert:
+ var extracted_elements := Array()
+ var args: Array = _extract_variadic_value(func_args)
+ var extractor := GdUnitFuncValueExtractor.new(func_name, args)
+ var current: Variant = get_current_value()
+ if current == null:
+ _current_value_provider = DefaultValueProvider.new(null)
+ else:
+ for element: Variant in current:
+ extracted_elements.append(extractor.extract_value(element))
+ _current_value_provider = DefaultValueProvider.new(extracted_elements)
+ return self
+
+
+func extractv(...extractors: Array) -> GdUnitArrayAssert:
+ var extracted_elements := Array()
+ var current: Variant = get_current_value()
+ if current == null:
+ _current_value_provider = DefaultValueProvider.new(null)
+ else:
+ for element: Variant in current:
+ var ev: Array[Variant] = []
+ for index: int in extractors.size():
+ var extractor: GdUnitValueExtractor = extractors[index]
+ ev.append(extractor.extract_value(element))
+ if extractors.size() > 1:
+ extracted_elements.append(GdUnitTuple.new.callv(ev))
+ else:
+ extracted_elements.append(ev[0])
+ _current_value_provider = DefaultValueProvider.new(extracted_elements)
+ return self
+
+
+## Small helper to support the old expected arguments as single array and variadic arguments
+func _extract_variadic_value(values: Variant) -> Variant:
+ @warning_ignore("unsafe_method_access")
+ if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]):
+ return values[0]
+ return values
+
+
+@warning_ignore("incompatible_ternary")
+func _is_equal(
+ left: Variant,
+ right: Variant,
+ case_sensitive := false,
+ compare_mode := GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:
+
+ @warning_ignore("unsafe_cast")
+ return GdObjects.equals(
+ (left as Array) if GdArrayTools.is_array_type(left) else left,
+ (right as Array) if GdArrayTools.is_array_type(right) else right,
+ case_sensitive,
+ compare_mode
+ )
+
+
+func _is_equals_sorted(
+ left: Variant,
+ right: Variant,
+ case_sensitive := false,
+ compare_mode := GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:
+
+ @warning_ignore("unsafe_cast")
+ return GdObjects.equals_sorted(
+ left as Array,
+ right as Array,
+ case_sensitive,
+ compare_mode)
diff --git a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid
new file mode 100644
index 0000000..83db7a3
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://cugrq3kd6cfsd
diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd
new file mode 100644
index 0000000..9f578c4
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd
@@ -0,0 +1,80 @@
+class_name GdUnitAssertImpl
+extends GdUnitAssert
+
+
+var _current :Variant
+var _current_failure_message :String = ""
+var _custom_failure_message :String = ""
+var _additional_failure_message: String = ""
+
+
+func _init(current :Variant) -> void:
+ _current = current
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ GdAssertReports.reset_last_error_line_number()
+
+
+
+func failure_message() -> String:
+ return _current_failure_message
+
+
+func current_value() -> Variant:
+ return _current
+
+
+func report_success() -> GdUnitAssert:
+ GdAssertReports.report_success()
+ return self
+
+
+func report_error(failure :String, failure_line_number: int = -1) -> GdUnitAssert:
+ var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number()
+ GdAssertReports.set_last_error_line_number(line_number)
+ _current_failure_message = GdAssertMessages.build_failure_message(failure, _additional_failure_message, _custom_failure_message)
+ GdAssertReports.report_error(_current_failure_message, line_number)
+ Engine.set_meta("GD_TEST_FAILURE", true)
+ return self
+
+
+func do_fail() -> GdUnitAssert:
+ return report_error(GdAssertMessages.error_not_implemented())
+
+
+func override_failure_message(message: String) -> GdUnitAssert:
+ _custom_failure_message = message
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitAssert:
+ _additional_failure_message = message
+ return self
+
+
+func is_null() -> GdUnitAssert:
+ var current :Variant = current_value()
+ if current != null:
+ return report_error(GdAssertMessages.error_is_null(current))
+ return report_success()
+
+
+func is_not_null() -> GdUnitAssert:
+ var current :Variant = current_value()
+ if current == null:
+ return report_error(GdAssertMessages.error_is_not_null())
+ return report_success()
+
+
+func is_equal(expected: Variant) -> GdUnitAssert:
+ var current: Variant = current_value()
+ if not GdObjects.equals(current, expected):
+ return report_error(GdAssertMessages.error_equal(current, expected))
+ return report_success()
+
+
+func is_not_equal(expected: Variant) -> GdUnitAssert:
+ var current: Variant = current_value()
+ if GdObjects.equals(current, expected):
+ return report_error(GdAssertMessages.error_not_equal(current, expected))
+ return report_success()
diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid
new file mode 100644
index 0000000..2ca96f7
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://dwhamqtf0w80b
diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd
new file mode 100644
index 0000000..a5b53c1
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd
@@ -0,0 +1,68 @@
+# Preloads all GdUnit assertions
+class_name GdUnitAssertions
+extends RefCounted
+
+
+@warning_ignore("return_value_discarded")
+func _init() -> void:
+ # preload all gdunit assertions to speedup testsuite loading time
+ # gdlint:disable=private-method-call
+ @warning_ignore_start("return_value_discarded")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd")
+ GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd")
+ @warning_ignore_restore("return_value_discarded")
+
+
+### We now load all used asserts and tool scripts into the cache according to the principle of "lazy loading"
+### in order to noticeably reduce the loading time of the test suite.
+# We go this hard way to increase the loading performance to avoid reparsing all the used scripts
+# for more detailed info -> https://github.com/godotengine/godot/issues/67400
+# gdlint:disable=function-name
+static func __lazy_load(script_path :String) -> GDScript:
+ return ResourceLoader.load(script_path, "GDScript", ResourceLoader.CACHE_MODE_REUSE)
+
+
+static func validate_value_type(value :Variant, type :Variant.Type) -> bool:
+ return value == null or typeof(value) == type
+
+
+# Scans the current stack trace for the root cause to extract the line number
+static func get_line_number() -> int:
+ var stack_trace := get_stack()
+ if stack_trace == null or stack_trace.is_empty():
+ return -1
+ for index in stack_trace.size():
+ var stack_info :Dictionary = stack_trace[index]
+ var function :String = stack_info.get("function")
+ # we catch helper asserts to skip over to return the correct line number
+ if function.begins_with("assert_"):
+ continue
+ if function.begins_with("test_"):
+ return stack_info.get("line")
+ var source :String = stack_info.get("source")
+ if source.is_empty() \
+ or source.begins_with("user://") \
+ or source.ends_with("GdUnitAssert.gd") \
+ or source.ends_with("GdUnitAssertions.gd") \
+ or source.ends_with("AssertImpl.gd") \
+ or source.ends_with("GdUnitTestSuite.gd") \
+ or source.ends_with("GdUnitSceneRunnerImpl.gd") \
+ or source.ends_with("GdUnitObjectInteractions.gd") \
+ or source.ends_with("GdUnitObjectInteractionsVerifier.gd") \
+ or source.ends_with("GdUnitAwaiter.gd"):
+ continue
+ return stack_info.get("line")
+ return -1
diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid
new file mode 100644
index 0000000..87e7ca5
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid
@@ -0,0 +1 @@
+uid://cp756mi1gtrjl
diff --git a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd
new file mode 100644
index 0000000..2fc0ce4
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd
@@ -0,0 +1,87 @@
+extends GdUnitBoolAssert
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current :Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not GdUnitAssertions.validate_value_type(current, TYPE_BOOL):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitBoolAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func current_value() -> Variant:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_null() -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_equal(expected)
+ return self
+
+
+func is_not_equal(expected: Variant) -> GdUnitBoolAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_equal(expected)
+ return self
+
+
+func is_true() -> GdUnitBoolAssert:
+ if current_value() != true:
+ return report_error(GdAssertMessages.error_is_true(current_value()))
+ return report_success()
+
+
+func is_false() -> GdUnitBoolAssert:
+ if current_value() == true || current_value() == null:
+ return report_error(GdAssertMessages.error_is_false(current_value()))
+ return report_success()
diff --git a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid
new file mode 100644
index 0000000..7cecf08
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://dxdyrrpo58p5x
diff --git a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd
new file mode 100644
index 0000000..c57bbc2
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd
@@ -0,0 +1,207 @@
+extends GdUnitDictionaryAssert
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current :Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not GdUnitAssertions.validate_value_type(current, TYPE_DICTIONARY):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitDictionaryAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func report_success() -> GdUnitDictionaryAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitDictionaryAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitDictionaryAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitDictionaryAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func current_value() -> Variant:
+ return _base.current_value()
+
+
+func is_null() -> GdUnitDictionaryAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitDictionaryAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ if current == null:
+ return report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected)))
+ if not GdObjects.equals(current, expected):
+ var c := GdAssertMessages.format_dict(current)
+ var e := GdAssertMessages.format_dict(expected)
+ return report_error(GdAssertMessages.error_equal(c, e))
+ return report_success()
+
+
+func is_not_equal(expected: Variant) -> GdUnitDictionaryAssert:
+ var current: Variant = current_value()
+ if GdObjects.equals(current, expected):
+ return report_error(GdAssertMessages.error_not_equal(current, expected))
+ return report_success()
+
+
+@warning_ignore("unused_parameter", "shadowed_global_identifier")
+func is_same(expected :Variant) -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ if current == null:
+ return report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected)))
+ if not is_same(current, expected):
+ var c := GdAssertMessages.format_dict(current)
+ var e := GdAssertMessages.format_dict(expected)
+ return report_error(GdAssertMessages.error_is_same(c, e))
+ return report_success()
+
+
+@warning_ignore("unused_parameter", "shadowed_global_identifier")
+func is_not_same(expected :Variant) -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ if is_same(current, expected):
+ return report_error(GdAssertMessages.error_not_same(current, expected))
+ return report_success()
+
+
+func is_empty() -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or not (current as Dictionary).is_empty():
+ return report_error(GdAssertMessages.error_is_empty(current))
+ return report_success()
+
+
+func is_not_empty() -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or (current as Dictionary).is_empty():
+ return report_error(GdAssertMessages.error_is_not_empty())
+ return report_success()
+
+
+func has_size(expected: int) -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ if current == null:
+ return report_error(GdAssertMessages.error_is_not_null())
+ @warning_ignore("unsafe_cast")
+ if (current as Dictionary).size() != expected:
+ return report_error(GdAssertMessages.error_has_size(current, expected))
+ return report_success()
+
+
+func _contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ var expected_value: Array = _extract_variadic_value(expected)
+ if current == null:
+ return report_error(GdAssertMessages.error_is_not_null())
+ # find expected keys
+ @warning_ignore("unsafe_cast")
+ var keys_not_found :Array = expected_value.filter(_filter_by_key.bind((current as Dictionary).keys(), compare_mode))
+ if not keys_not_found.is_empty():
+ @warning_ignore("unsafe_cast")
+ return report_error(GdAssertMessages.error_contains_keys((current as Dictionary).keys() as Array, expected_value,
+ keys_not_found, compare_mode))
+ return report_success()
+
+
+func _contains_key_value(key :Variant, value :Variant, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ var expected := [key]
+ if current == null:
+ return report_error(GdAssertMessages.error_is_not_null())
+ var dict_current: Dictionary = current
+ var keys_not_found :Array = expected.filter(_filter_by_key.bind(dict_current.keys(), compare_mode))
+ if not keys_not_found.is_empty():
+ return report_error(GdAssertMessages.error_contains_keys(dict_current.keys() as Array, expected, keys_not_found, compare_mode))
+ if not GdObjects.equals(dict_current[key], value, false, compare_mode):
+ return report_error(GdAssertMessages.error_contains_key_value(key, value, dict_current[key], compare_mode))
+ return report_success()
+
+
+func _not_contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:
+ var current :Variant = current_value()
+ var expected_value: Array = _extract_variadic_value(expected)
+ if current == null:
+ return report_error(GdAssertMessages.error_is_not_null())
+ var dict_current: Dictionary = current
+ var keys_found :Array = dict_current.keys().filter(_filter_by_key.bind(expected_value, compare_mode, true))
+ if not keys_found.is_empty():
+ return report_error(GdAssertMessages.error_not_contains_keys(dict_current.keys() as Array, expected_value, keys_found, compare_mode))
+ return report_success()
+
+
+func contains_keys(...expected: Array) -> GdUnitDictionaryAssert:
+ return _contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+
+
+func contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:
+ return _contains_key_value(key, value, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+
+
+func not_contains_keys(...expected: Array) -> GdUnitDictionaryAssert:
+ return _not_contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
+
+
+func contains_same_keys(expected :Array) -> GdUnitDictionaryAssert:
+ return _contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
+
+
+func contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:
+ return _contains_key_value(key, value, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
+
+
+func not_contains_same_keys(...expected: Array) -> GdUnitDictionaryAssert:
+ return _not_contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
+
+
+func _filter_by_key(element :Variant, values :Array, compare_mode :GdObjects.COMPARE_MODE, is_not :bool = false) -> bool:
+ for key :Variant in values:
+ if GdObjects.equals(key, element, false, compare_mode):
+ return is_not
+ return !is_not
+
+
+## Small helper to support the old expected arguments as single array and variadic arguments
+func _extract_variadic_value(values: Variant) -> Variant:
+ @warning_ignore("unsafe_method_access")
+ if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]):
+ return values[0]
+ return values
diff --git a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid
new file mode 100644
index 0000000..4ba626d
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://cxrjs0mlg1qr0
diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd
new file mode 100644
index 0000000..198624c
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd
@@ -0,0 +1,136 @@
+extends GdUnitFailureAssert
+
+const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd")
+
+var _is_failed := false
+var _failure_message: String
+var _current_failure_message := ""
+var _custom_failure_message := ""
+var _additional_failure_message := ""
+
+
+func _set_do_expect_fail(enabled :bool = true) -> void:
+ Engine.set_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES, enabled)
+
+
+func execute_and_await(assertion :Callable, do_await := true) -> GdUnitFailureAssert:
+ # do not report any failure from the original assertion we want to test
+ _set_do_expect_fail(true)
+ var thread_context := GdUnitThreadManager.get_current_context()
+ thread_context.set_assert(null)
+ @warning_ignore("return_value_discarded")
+ GdUnitSignals.instance().gdunit_set_test_failed.connect(_on_test_failed)
+ # execute the given assertion as callable
+ if do_await:
+ await assertion.call()
+ else:
+ assertion.call()
+ _set_do_expect_fail(false)
+ # get the assert instance from current tread context
+ var current_assert := thread_context.get_assert()
+ if not is_instance_of(current_assert, GdUnitAssert):
+ _is_failed = true
+ _failure_message = "Invalid Callable! It must be a callable of 'GdUnitAssert'"
+ return self
+ @warning_ignore("unsafe_method_access")
+ _failure_message = current_assert.failure_message()
+ return self
+
+
+func execute(assertion :Callable) -> GdUnitFailureAssert:
+ @warning_ignore("return_value_discarded")
+ execute_and_await(assertion, false)
+ return self
+
+
+func _on_test_failed(value :bool) -> void:
+ _is_failed = value
+
+
+func is_equal(_expected: Variant) -> GdUnitFailureAssert:
+ return _report_error("Not implemented")
+
+
+func is_not_equal(_expected: Variant) -> GdUnitFailureAssert:
+ return _report_error("Not implemented")
+
+
+func is_null() -> GdUnitFailureAssert:
+ return _report_error("Not implemented")
+
+
+func is_not_null() -> GdUnitFailureAssert:
+ return _report_error("Not implemented")
+
+
+func override_failure_message(message: String) -> GdUnitFailureAssert:
+ _custom_failure_message = message
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitFailureAssert:
+ _additional_failure_message = message
+ return self
+
+
+func is_success() -> GdUnitFailureAssert:
+ if _is_failed:
+ return _report_error("Expect: assertion ends successfully.")
+ return self
+
+
+func is_failed() -> GdUnitFailureAssert:
+ if not _is_failed:
+ return _report_error("Expect: assertion fails.")
+ return self
+
+
+func has_line(expected :int) -> GdUnitFailureAssert:
+ var current := GdAssertReports.get_last_error_line_number()
+ if current != expected:
+ return _report_error("Expect: to failed on line '%d'\n but was '%d'." % [expected, current])
+ return self
+
+
+func has_message(expected :String) -> GdUnitFailureAssert:
+ @warning_ignore("return_value_discarded")
+ is_failed()
+ var expected_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(expected))
+ var current_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(_failure_message))
+ if current_error != expected_error:
+ var diffs := GdDiffTool.string_diff(current_error, expected_error)
+ var current := GdAssertMessages.colored_array_div(diffs[1])
+ return _report_error(GdAssertMessages.error_not_same_error(current, expected_error))
+ return self
+
+
+func contains_message(expected :String) -> GdUnitFailureAssert:
+ var expected_error := GdUnitTools.normalize_text(expected)
+ var current_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(_failure_message))
+ if not current_error.contains(expected_error):
+ var diffs := GdDiffTool.string_diff(current_error, expected_error)
+ var current := GdAssertMessages.colored_array_div(diffs[1])
+ return _report_error(GdAssertMessages.error_not_same_error(current, expected_error))
+ return self
+
+
+func starts_with_message(expected :String) -> GdUnitFailureAssert:
+ var expected_error := GdUnitTools.normalize_text(expected)
+ var current_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(_failure_message))
+ if current_error.find(expected_error) != 0:
+ var diffs := GdDiffTool.string_diff(current_error, expected_error)
+ var current := GdAssertMessages.colored_array_div(diffs[1])
+ return _report_error(GdAssertMessages.error_not_same_error(current, expected_error))
+ return self
+
+
+func _report_error(error_message :String, failure_line_number: int = -1) -> GdUnitAssert:
+ var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number()
+ _current_failure_message = GdAssertMessages.build_failure_message(error_message, _additional_failure_message, _custom_failure_message)
+ GdAssertReports.report_error(_current_failure_message, line_number)
+ return self
+
+
+func _report_success() -> GdUnitFailureAssert:
+ GdAssertReports.report_success()
+ return self
diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid
new file mode 100644
index 0000000..cf78a00
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://cr53a1nnhmaqa
diff --git a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd
new file mode 100644
index 0000000..c4f9570
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd
@@ -0,0 +1,116 @@
+extends GdUnitFileAssert
+
+const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd")
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current :Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not GdUnitAssertions.validate_value_type(current, TYPE_STRING):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitFileAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func current_value() -> String:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_null() -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_equal(expected)
+ return self
+
+
+func is_not_equal(expected: Variant) -> GdUnitFileAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_equal(expected)
+ return self
+
+
+func is_file() -> GdUnitFileAssert:
+ var current := current_value()
+ if FileAccess.open(current, FileAccess.READ) == null:
+ return report_error("Is not a file '%s', error code %s" % [current, FileAccess.get_open_error()])
+ return report_success()
+
+
+func exists() -> GdUnitFileAssert:
+ var current := current_value()
+ if not FileAccess.file_exists(current):
+ return report_error("The file '%s' not exists" %current)
+ return report_success()
+
+
+func is_script() -> GdUnitFileAssert:
+ var current := current_value()
+ if FileAccess.open(current, FileAccess.READ) == null:
+ return report_error("Can't acces the file '%s'! Error code %s" % [current, FileAccess.get_open_error()])
+
+ var script := load(current)
+ if not script is GDScript:
+ return report_error("The file '%s' is not a GdScript" % current)
+ return report_success()
+
+
+func contains_exactly(expected_rows: Array) -> GdUnitFileAssert:
+ var current := current_value()
+ if FileAccess.open(current, FileAccess.READ) == null:
+ return report_error("Can't acces the file '%s'! Error code %s" % [current, FileAccess.get_open_error()])
+
+ var script: GDScript = load(current)
+ if script is GDScript:
+ var source_code := GdScriptParser.to_unix_format(script.source_code)
+ var rows := Array(source_code.split("\n"))
+ @warning_ignore("return_value_discarded")
+ GdUnitArrayAssertImpl.new(rows).contains_exactly(expected_rows)
+ return self
diff --git a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid
new file mode 100644
index 0000000..b8360b7
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://mr5x0e3q4g0a
diff --git a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd
new file mode 100644
index 0000000..83d7e05
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd
@@ -0,0 +1,159 @@
+extends GdUnitFloatAssert
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current :Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not GdUnitAssertions.validate_value_type(current, TYPE_FLOAT):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitFloatAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func current_value() -> Variant:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_null() -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_equal(expected)
+ return self
+
+
+func is_not_equal(expected: Variant) -> GdUnitFloatAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_equal(expected)
+ return self
+
+
+@warning_ignore("shadowed_global_identifier")
+func is_equal_approx(expected :float, approx :float) -> GdUnitFloatAssert:
+ return is_between(expected-approx, expected+approx)
+
+
+func is_less(expected :float) -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if current == null or current >= expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.LESS_THAN, current, expected))
+ return report_success()
+
+
+func is_less_equal(expected :float) -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if current == null or current > expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.LESS_EQUAL, current, expected))
+ return report_success()
+
+
+func is_greater(expected :float) -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if current == null or current <= expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.GREATER_THAN, current, expected))
+ return report_success()
+
+
+func is_greater_equal(expected :float) -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if current == null or current < expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.GREATER_EQUAL, current, expected))
+ return report_success()
+
+
+func is_negative() -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if current == null or current >= 0.0:
+ return report_error(GdAssertMessages.error_is_negative(current))
+ return report_success()
+
+
+func is_not_negative() -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if current == null or current < 0.0:
+ return report_error(GdAssertMessages.error_is_not_negative(current))
+ return report_success()
+
+
+func is_zero() -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or not is_equal_approx(0.00000000, current as float):
+ return report_error(GdAssertMessages.error_is_zero(current))
+ return report_success()
+
+
+func is_not_zero() -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or is_equal_approx(0.00000000, current as float):
+ return report_error(GdAssertMessages.error_is_not_zero())
+ return report_success()
+
+
+func is_in(expected :Array) -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if not expected.has(current):
+ return report_error(GdAssertMessages.error_is_in(current, expected))
+ return report_success()
+
+
+func is_not_in(expected :Array) -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if expected.has(current):
+ return report_error(GdAssertMessages.error_is_not_in(current, expected))
+ return report_success()
+
+
+func is_between(from :float, to :float) -> GdUnitFloatAssert:
+ var current :Variant = current_value()
+ if current == null or current < from or current > to:
+ return report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))
+ return report_success()
diff --git a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid
new file mode 100644
index 0000000..3d0dc07
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://cj7rei3qtlurx
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"
diff --git a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid
new file mode 100644
index 0000000..b10c045
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://cd337pgd74ed0
diff --git a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd
new file mode 100644
index 0000000..ce5503d
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd
@@ -0,0 +1,151 @@
+extends GdUnitGodotErrorAssert
+
+var _current_failure_message := ""
+var _custom_failure_message := ""
+var _additional_failure_message := ""
+var _callable: Callable
+var _logger := GodotGdErrorMonitor.GdUnitLogger.new(true, true)
+
+
+func _init(callable: Callable) -> void:
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ GdAssertReports.reset_last_error_line_number()
+ _callable = callable
+
+
+func _execute() -> Array[ErrorLogEntry]:
+ await _callable.call()
+ return _logger.entries()
+
+
+func failure_message() -> String:
+ return _current_failure_message
+
+
+func _report_success() -> GdUnitAssert:
+ GdAssertReports.report_success()
+ return self
+
+
+func _report_error(error_message: String, failure_line_number: int = -1) -> GdUnitAssert:
+ var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number()
+ _current_failure_message = GdAssertMessages.build_failure_message(error_message, _additional_failure_message, _custom_failure_message)
+ GdAssertReports.report_error(_current_failure_message, line_number)
+ return self
+
+
+func _has_log_entry(log_entries: Array[ErrorLogEntry], type: ErrorLogEntry.TYPE, error: Variant) -> bool:
+ for entry in log_entries:
+ if entry._type == type and GdObjects.equals(entry._message, error):
+ GdUnitThreadManager.get_current_context().get_execution_context().error_monitor.erase_log_entry(entry)
+ return true
+ return false
+
+
+func _to_list(log_entries: Array[ErrorLogEntry]) -> String:
+ if log_entries.is_empty():
+ return "no errors"
+
+ var values := []
+ for entry in log_entries:
+ values.append(entry)
+ return "\n".join(values)
+
+
+func is_null() -> GdUnitGodotErrorAssert:
+ return _report_error("Not implemented")
+
+
+func is_not_null() -> GdUnitGodotErrorAssert:
+ return _report_error("Not implemented")
+
+
+func is_equal(_expected: Variant) -> GdUnitGodotErrorAssert:
+ return _report_error("Not implemented")
+
+
+func is_not_equal(_expected: Variant) -> GdUnitGodotErrorAssert:
+ return _report_error("Not implemented")
+
+
+func override_failure_message(message: String) -> GdUnitGodotErrorAssert:
+ _custom_failure_message = message
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitGodotErrorAssert:
+ _additional_failure_message = message
+ return self
+
+
+func is_success() -> GdUnitGodotErrorAssert:
+ if not _validate_callable():
+ return self
+
+ var log_entries := await _execute()
+ if log_entries.is_empty():
+ return _report_success()
+ return _report_error("""
+ Expecting: no error's are ocured.
+ but found: '%s'
+ """.dedent().trim_prefix("\n") % _to_list(log_entries))
+
+
+func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
+ if not _validate_callable():
+ return self
+
+ var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error)
+ if result.is_error():
+ return _report_error(result.error_message())
+ var log_entries := await _execute()
+ if _has_log_entry(log_entries, ErrorLogEntry.TYPE.SCRIPT_ERROR, expected_error):
+ return _report_success()
+ return _report_error("""
+ Expecting: a runtime error is triggered.
+ expected: '%s'
+ current: '%s'
+ """.dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)])
+
+
+func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert:
+ if not _validate_callable():
+ return self
+
+ var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_warning)
+ if result.is_error():
+ return _report_error(result.error_message())
+ var log_entries := await _execute()
+ if _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_WARNING, expected_warning):
+ return _report_success()
+ return _report_error("""
+ Expecting: push_warning() is called.
+ expected: '%s'
+ current: '%s'
+ """.dedent().trim_prefix("\n") % [expected_warning, _to_list(log_entries)])
+
+
+func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
+ if not _validate_callable():
+ return self
+
+ var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error)
+ if result.is_error():
+ return _report_error(result.error_message())
+ var log_entries := await _execute()
+ if _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_ERROR, expected_error):
+ return _report_success()
+ return _report_error("""
+ Expecting: push_error() is called.
+ expected: '%s'
+ current: '%s'
+ """.dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)])
+
+
+func _validate_callable() -> bool:
+ if _callable == null or not _callable.is_valid():
+ @warning_ignore("return_value_discarded")
+ _report_error("Invalid Callable '%s'" % _callable)
+ return false
+ return true
diff --git a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid
new file mode 100644
index 0000000..8ecadb7
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://rkf7jw6mw4d4
diff --git a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd
new file mode 100644
index 0000000..bdee249
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd
@@ -0,0 +1,166 @@
+extends GdUnitIntAssert
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current :Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not GdUnitAssertions.validate_value_type(current, TYPE_INT):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitIntAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func current_value() -> Variant:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_null() -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_equal(expected)
+ return self
+
+
+func is_not_equal(expected: Variant) -> GdUnitIntAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_equal(expected)
+ return self
+
+
+func is_less(expected :int) -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current >= expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.LESS_THAN, current, expected))
+ return report_success()
+
+
+func is_less_equal(expected :int) -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current > expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.LESS_EQUAL, current, expected))
+ return report_success()
+
+
+func is_greater(expected :int) -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current <= expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.GREATER_THAN, current, expected))
+ return report_success()
+
+
+func is_greater_equal(expected :int) -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current < expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.GREATER_EQUAL, current, expected))
+ return report_success()
+
+
+func is_even() -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current % 2 != 0:
+ return report_error(GdAssertMessages.error_is_even(current))
+ return report_success()
+
+
+func is_odd() -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current % 2 == 0:
+ return report_error(GdAssertMessages.error_is_odd(current))
+ return report_success()
+
+
+func is_negative() -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current >= 0:
+ return report_error(GdAssertMessages.error_is_negative(current))
+ return report_success()
+
+
+func is_not_negative() -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current < 0:
+ return report_error(GdAssertMessages.error_is_not_negative(current))
+ return report_success()
+
+
+func is_zero() -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current != 0:
+ return report_error(GdAssertMessages.error_is_zero(current))
+ return report_success()
+
+
+func is_not_zero() -> GdUnitIntAssert:
+ var current :Variant= current_value()
+ if current == 0:
+ return report_error(GdAssertMessages.error_is_not_zero())
+ return report_success()
+
+
+func is_in(expected :Array) -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if not expected.has(current):
+ return report_error(GdAssertMessages.error_is_in(current, expected))
+ return report_success()
+
+
+func is_not_in(expected :Array) -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if expected.has(current):
+ return report_error(GdAssertMessages.error_is_not_in(current, expected))
+ return report_success()
+
+
+func is_between(from :int, to :int) -> GdUnitIntAssert:
+ var current :Variant = current_value()
+ if current == null or current < from or current > to:
+ return report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))
+ return report_success()
diff --git a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid
new file mode 100644
index 0000000..512cc48
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://dbms8smcdukg0
diff --git a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd
new file mode 100644
index 0000000..955e1ef
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd
@@ -0,0 +1,166 @@
+extends GdUnitObjectAssert
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current: Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if (current != null
+ and (GdUnitAssertions.validate_value_type(current, TYPE_BOOL)
+ or GdUnitAssertions.validate_value_type(current, TYPE_INT)
+ or GdUnitAssertions.validate_value_type(current, TYPE_FLOAT)
+ or GdUnitAssertions.validate_value_type(current, TYPE_STRING))):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitObjectAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event: int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func current_value() -> Variant:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error: String) -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_equal(expected)
+ return self
+
+
+func is_not_equal(expected: Variant) -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_equal(expected)
+ return self
+
+
+func is_null() -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitObjectAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+@warning_ignore("shadowed_global_identifier")
+func is_same(expected: Variant) -> GdUnitObjectAssert:
+ var current: Variant = current_value()
+ if not is_same(current, expected):
+ return report_error(GdAssertMessages.error_is_same(current, expected))
+ return report_success()
+
+
+func is_not_same(expected: Variant) -> GdUnitObjectAssert:
+ var current: Variant = current_value()
+ if is_same(current, expected):
+ return report_error(GdAssertMessages.error_not_same(current, expected))
+ return report_success()
+
+
+func is_instanceof(type: Variant) -> GdUnitObjectAssert:
+ var current: Variant = current_value()
+ if current == null or not is_instance_of(current, type):
+ var result_expected := GdObjects.extract_class_name(type)
+ var result_current := GdObjects.extract_class_name(current)
+ return report_error(GdAssertMessages.error_is_instanceof(result_current, result_expected))
+ return report_success()
+
+
+func is_not_instanceof(type: Variant) -> GdUnitObjectAssert:
+ var current: Variant = current_value()
+ if is_instance_of(current, type):
+ var result := GdObjects.extract_class_name(type)
+ if result.is_success():
+ return report_error("Expected not be a instance of <%s>" % str(result.value()))
+
+ push_error("Internal ERROR: %s" % result.error_message())
+ return self
+ return report_success()
+
+
+## Checks whether the current object inherits from the specified type.
+func is_inheriting(type: Variant) -> GdUnitObjectAssert:
+ var current: Variant = current_value()
+ if not is_instance_of(current, TYPE_OBJECT):
+ return report_error("Expected '%s' to inherit from at least Object." % str(current))
+ var result := _inherits(current, type)
+ if result.is_success():
+ return report_success()
+ return report_error(result.error_message())
+
+
+## Checks whether the current object does NOT inherit from the specified type.
+func is_not_inheriting(type: Variant) -> GdUnitObjectAssert:
+ var current: Variant = current_value()
+ if not is_instance_of(current, TYPE_OBJECT):
+ return report_error("Expected '%s' to inherit from at least Object." % str(current))
+ var result := _inherits(current, type)
+ if result.is_success():
+ return report_error("Expected type to not inherit from <%s>" % _extract_class_type(type))
+ return report_success()
+
+
+func _inherits(current: Variant, type: Variant) -> GdUnitResult:
+ var type_as_string := _extract_class_type(type)
+ if type_as_string == "Object":
+ return GdUnitResult.success("")
+
+ var obj: Object = current
+ for p in obj.get_property_list():
+ var clazz_name :String = p["name"]
+ if p["usage"] == PROPERTY_USAGE_CATEGORY and clazz_name == p["hint_string"] and clazz_name == type_as_string:
+ return GdUnitResult.success("")
+ var script: Script = obj.get_script()
+ if script != null:
+ while script != null:
+ var result := GdObjects.extract_class_name(script)
+ if result.is_success() and result.value() == type_as_string:
+ return GdUnitResult.success("")
+ script = script.get_base_script()
+ return GdUnitResult.error("Expected type to inherit from <%s>" % type_as_string)
+
+
+func _extract_class_type(type: Variant) -> String:
+ if type is String:
+ return type
+ var result := GdObjects.extract_class_name(type)
+ if result.is_error():
+ return ""
+ return result.value()
diff --git a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid
new file mode 100644
index 0000000..349ff7b
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://biqm7ejvhb4oc
diff --git a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd
new file mode 100644
index 0000000..98a6768
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd
@@ -0,0 +1,128 @@
+extends GdUnitResultAssert
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current :Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not validate_value_type(current):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitResultAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func validate_value_type(value :Variant) -> bool:
+ return value == null or value is GdUnitResult
+
+
+func current_value() -> GdUnitResult:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitResultAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitResultAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitResultAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitResultAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_null() -> GdUnitResultAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitResultAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitResultAssert:
+ return is_value(expected)
+
+
+func is_not_equal(expected: Variant) -> GdUnitResultAssert:
+ var result := current_value()
+ var value :Variant = null if result == null else result.value()
+ if GdObjects.equals(value, expected):
+ return report_error(GdAssertMessages.error_not_equal(value, expected))
+ return report_success()
+
+
+func is_empty() -> GdUnitResultAssert:
+ var result := current_value()
+ if result == null or not result.is_empty():
+ return report_error(GdAssertMessages.error_result_is_empty(result))
+ return report_success()
+
+
+func is_success() -> GdUnitResultAssert:
+ var result := current_value()
+ if result == null or not result.is_success():
+ return report_error(GdAssertMessages.error_result_is_success(result))
+ return report_success()
+
+
+func is_warning() -> GdUnitResultAssert:
+ var result := current_value()
+ if result == null or not result.is_warn():
+ return report_error(GdAssertMessages.error_result_is_warning(result))
+ return report_success()
+
+
+func is_error() -> GdUnitResultAssert:
+ var result := current_value()
+ if result == null or not result.is_error():
+ return report_error(GdAssertMessages.error_result_is_error(result))
+ return report_success()
+
+
+func contains_message(expected :String) -> GdUnitResultAssert:
+ var result := current_value()
+ if result == null:
+ return report_error(GdAssertMessages.error_result_has_message("<null>", expected))
+ if result.is_success():
+ return report_error(GdAssertMessages.error_result_has_message_on_success(expected))
+ if result.is_error() and result.error_message() != expected:
+ return report_error(GdAssertMessages.error_result_has_message(result.error_message(), expected))
+ if result.is_warn() and result.warn_message() != expected:
+ return report_error(GdAssertMessages.error_result_has_message(result.warn_message(), expected))
+ return report_success()
+
+
+func is_value(expected: Variant) -> GdUnitResultAssert:
+ var result := current_value()
+ var value :Variant = null if result == null else result.value()
+ if not GdObjects.equals(value, expected):
+ return report_error(GdAssertMessages.error_result_is_value(value, expected))
+ return report_success()
diff --git a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid
new file mode 100644
index 0000000..6ba023e
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://bpeci78q50dtq
diff --git a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd
new file mode 100644
index 0000000..0da696a
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd
@@ -0,0 +1,172 @@
+extends GdUnitSignalAssert
+
+const DEFAULT_TIMEOUT := 2000
+
+var _signal_collector :GdUnitSignalCollector
+var _emitter :Object
+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
+
+
+func _init(emitter :Object) -> void:
+ # save the actual assert instance on the current thread context
+ var context := GdUnitThreadManager.get_current_context()
+ context.set_assert(self)
+ _signal_collector = context.get_signal_collector()
+ _line_number = GdUnitAssertions.get_line_number()
+ _emitter = emitter
+ GdAssertReports.reset_last_error_line_number()
+
+
+func _notification(what :int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ _interrupted = true
+ if is_instance_valid(_emitter):
+ _signal_collector.unregister_emitter(_emitter)
+ _emitter = null
+
+
+func report_success() -> GdUnitAssert:
+ GdAssertReports.report_success()
+ return self
+
+
+func report_warning(message :String) -> GdUnitAssert:
+ GdAssertReports.report_warning(message, GdUnitAssertions.get_line_number())
+ return self
+
+
+func report_error(failure :String) -> GdUnitAssert:
+ _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) -> GdUnitSignalAssert:
+ _custom_failure_message = message
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitSignalAssert:
+ _additional_failure_message = message
+ return self
+
+
+func wait_until(timeout := 2000) -> GdUnitSignalAssert:
+ if timeout <= 0:
+ @warning_ignore("return_value_discarded")
+ report_warning("Invalid timeout parameter, allowed timeouts must be greater than 0, use default timeout instead!")
+ _timeout = DEFAULT_TIMEOUT
+ else:
+ _timeout = timeout
+ return self
+
+
+func is_null() -> GdUnitSignalAssert:
+ if _emitter != null:
+ return report_error(GdAssertMessages.error_is_null(_emitter))
+ return report_success()
+
+
+func is_not_null() -> GdUnitSignalAssert:
+ if _emitter == null:
+ return report_error(GdAssertMessages.error_is_not_null())
+ return report_success()
+
+
+func is_equal(_expected: Variant) -> GdUnitSignalAssert:
+ return report_error("Not implemented")
+
+
+func is_not_equal(_expected: Variant) -> GdUnitSignalAssert:
+ return report_error("Not implemented")
+
+
+# Verifies the signal exists checked the emitter
+func is_signal_exists(signal_or_name: Variant) -> GdUnitSignalAssert:
+ if not (signal_or_name is String or signal_or_name is Signal):
+ return report_error("Invalid signal_name: expected String or Signal, but is '%s'" % type_string(typeof(signal_or_name)))
+
+ var signal_name := _to_signal_name(signal_or_name)
+
+ if not _emitter.has_signal(signal_name):
+ @warning_ignore("return_value_discarded")
+ report_error("The signal '%s' not exists checked object '%s'." % [signal_name, _emitter.get_class()])
+ return self
+
+
+# Verifies that given signal is emitted until waiting time
+func is_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert:
+ _line_number = GdUnitAssertions.get_line_number()
+ @warning_ignore("unsafe_call_argument")
+ return await _wail_until_signal(
+ signal_name,
+ _wrap_arguments.callv(signal_args),
+ false)
+
+
+# Verifies that given signal is NOT emitted until waiting time
+func is_not_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert:
+ _line_number = GdUnitAssertions.get_line_number()
+ @warning_ignore("unsafe_call_argument")
+ return await _wail_until_signal(
+ signal_name,
+ _wrap_arguments.callv(signal_args),
+ true)
+
+
+func _wrap_arguments(...args: Array) -> Array:
+ # Check using old syntax
+ if not args.is_empty() and args[0] is Array:
+ return args[0]
+ return args
+
+
+func _wail_until_signal(signal_or_name: Variant, expected_args: Array, expect_not_emitted: bool) -> GdUnitSignalAssert:
+ if _emitter == null:
+ return report_error("Can't wait for signal checked a NULL object.")
+ if not (signal_or_name is String or signal_or_name is Signal):
+ return report_error("Invalid signal_name: expected String or Signal, but is '%s'" % type_string(typeof(signal_or_name)))
+
+ var signal_name := _to_signal_name(signal_or_name)
+ # first verify the signal is defined
+ if not _emitter.has_signal(signal_name):
+ return report_error("Can't wait for non-existion signal '%s' checked object '%s'." % [signal_name,_emitter.get_class()])
+ _signal_collector.register_emitter(_emitter)
+ var time_scale := Engine.get_time_scale()
+ var timer := Timer.new()
+ (Engine.get_main_loop() as SceneTree).root.add_child(timer)
+ timer.add_to_group("GdUnitTimers")
+ timer.set_one_shot(true)
+ @warning_ignore("return_value_discarded")
+ timer.timeout.connect(func on_timeout() -> void: _interrupted = true)
+ timer.start((_timeout/1000.0)*time_scale)
+ var is_signal_emitted := false
+ while not _interrupted and not is_signal_emitted:
+ await (Engine.get_main_loop() as SceneTree).process_frame
+ if is_instance_valid(_emitter):
+ is_signal_emitted = _signal_collector.match(_emitter, signal_name, expected_args)
+ if is_signal_emitted and expect_not_emitted:
+ @warning_ignore("return_value_discarded")
+ report_error(GdAssertMessages.error_signal_emitted(signal_name, expected_args, LocalTime.elapsed(int(_timeout-timer.time_left*1000))))
+
+ if _interrupted and not expect_not_emitted:
+ @warning_ignore("return_value_discarded")
+ report_error(GdAssertMessages.error_wait_signal(signal_name, expected_args, LocalTime.elapsed(_timeout)))
+ timer.free()
+ if is_instance_valid(_emitter):
+ _signal_collector.reset_received_signals(_emitter, signal_name, expected_args)
+ return self
+
+
+func _to_signal_name(signal_or_name: Variant) -> String:
+ @warning_ignore("unsafe_cast")
+ return (signal_or_name as Signal).get_name() if signal_or_name is Signal else signal_or_name
diff --git a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid
new file mode 100644
index 0000000..3690588
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://n33x4jg0yjqh
diff --git a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd
new file mode 100644
index 0000000..cdbcdff
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd
@@ -0,0 +1,208 @@
+extends GdUnitStringAssert
+
+var _base: GdUnitAssertImpl
+
+
+func _init(current :Variant) -> void:
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if current != null and typeof(current) != TYPE_STRING and typeof(current) != TYPE_STRING_NAME:
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitStringAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func current_value() -> Variant:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitStringAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitStringAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func override_failure_message(message: String) -> GdUnitStringAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message: String) -> GdUnitStringAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_null() -> GdUnitStringAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitStringAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitStringAssert:
+ return _is_equal(expected, false, GdAssertMessages.error_equal)
+
+
+func is_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert:
+ return _is_equal(expected, true, GdAssertMessages.error_equal_ignoring_case)
+
+
+@warning_ignore_start("unsafe_call_argument")
+func _is_equal(expected: Variant, ignore_case: bool, message_cb: Callable) -> GdUnitStringAssert:
+ var current: Variant = current_value()
+ if current == null:
+ return report_error(message_cb.call(current, expected))
+ var cur_value := str(current)
+ if not GdObjects.equals(cur_value, expected, ignore_case):
+ var exp_value := str(expected)
+ if contains_bbcode(cur_value):
+ # mask user bbcode
+ # https://docs.godotengine.org/en/4.5/tutorials/ui/bbcode_in_richtextlabel.html#handling-user-input-safely
+ return report_error(message_cb.call(cur_value.replace("[", "[lb]"), exp_value.replace("[", "[lb]")))
+ var diffs := GdDiffTool.string_diff(cur_value, exp_value)
+ var formatted_current := GdAssertMessages.colored_array_div(diffs[1])
+ return report_error(message_cb.call(formatted_current, exp_value))
+ return report_success()
+@warning_ignore_restore("unsafe_call_argument")
+
+
+func is_not_equal(expected: Variant) -> GdUnitStringAssert:
+ var current: Variant = current_value()
+ if GdObjects.equals(current, expected):
+ return report_error(GdAssertMessages.error_not_equal(current, expected))
+ return report_success()
+
+
+func is_not_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ if GdObjects.equals(current, expected, true):
+ return report_error(GdAssertMessages.error_not_equal(current, expected))
+ return report_success()
+
+
+func is_empty() -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or not (current as String).is_empty():
+ return report_error(GdAssertMessages.error_is_empty(current))
+ return report_success()
+
+
+func is_not_empty() -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or (current as String).is_empty():
+ return report_error(GdAssertMessages.error_is_not_empty())
+ return report_success()
+
+
+func contains(expected :String) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or (current as String).find(expected) == -1:
+ return report_error(GdAssertMessages.error_contains(current, expected))
+ return report_success()
+
+
+func not_contains(expected :String) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current != null and (current as String).find(expected) != -1:
+ return report_error(GdAssertMessages.error_not_contains(current, expected))
+ return report_success()
+
+
+func contains_ignoring_case(expected :String) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or (current as String).findn(expected) == -1:
+ return report_error(GdAssertMessages.error_contains_ignoring_case(current, expected))
+ return report_success()
+
+
+func not_contains_ignoring_case(expected :String) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current != null and (current as String).findn(expected) != -1:
+ return report_error(GdAssertMessages.error_not_contains_ignoring_case(current, expected))
+ return report_success()
+
+
+func starts_with(expected :String) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ @warning_ignore("unsafe_cast")
+ if current == null or (current as String).find(expected) != 0:
+ return report_error(GdAssertMessages.error_starts_with(current, expected))
+ return report_success()
+
+
+func ends_with(expected :String) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ if current == null:
+ return report_error(GdAssertMessages.error_ends_with(current, expected))
+ @warning_ignore("unsafe_cast")
+ var find :int = (current as String).length() - expected.length()
+ @warning_ignore("unsafe_cast")
+ if (current as String).rfind(expected) != find:
+ return report_error(GdAssertMessages.error_ends_with(current, expected))
+ return report_success()
+
+
+# gdlint:disable=max-returns
+func has_length(expected :int, comparator := Comparator.EQUAL) -> GdUnitStringAssert:
+ var current :Variant = current_value()
+ if current == null:
+ return report_error(GdAssertMessages.error_has_length(current, expected, comparator))
+ var str_current: String = current
+ match comparator:
+ Comparator.EQUAL:
+ if str_current.length() != expected:
+ return report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))
+ Comparator.LESS_THAN:
+ if str_current.length() >= expected:
+ return report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))
+ Comparator.LESS_EQUAL:
+ if str_current.length() > expected:
+ return report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))
+ Comparator.GREATER_THAN:
+ if str_current.length() <= expected:
+ return report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))
+ Comparator.GREATER_EQUAL:
+ if str_current.length() < expected:
+ return report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))
+ _:
+ return report_error("Comparator '%d' not implemented!" % comparator)
+ return report_success()
+
+
+func contains_bbcode(value: String) -> bool:
+ var rtl := RichTextLabel.new()
+ rtl.bbcode_enabled = true
+ rtl.parse_bbcode(value)
+ var has_bbcode := rtl.get_parsed_text() != value
+ rtl.free()
+ return has_bbcode
diff --git a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid
new file mode 100644
index 0000000..d257a25
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://cp0rvxafx8aqw
diff --git a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd
new file mode 100644
index 0000000..fbc031a
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd
@@ -0,0 +1,187 @@
+extends GdUnitVectorAssert
+
+var _base: GdUnitAssertImpl
+var _current_type: int
+var _type_check: bool
+
+func _init(current: Variant, type_check := true) -> void:
+ _type_check = type_check
+ _base = GdUnitAssertImpl.new(current)
+ # save the actual assert instance on the current thread context
+ GdUnitThreadManager.get_current_context().set_assert(self)
+ if not _validate_value_type(current):
+ @warning_ignore("return_value_discarded")
+ report_error("GdUnitVectorAssert error, the type <%s> is not supported." % GdObjects.typeof_as_string(current))
+ _current_type = typeof(current)
+
+
+func _notification(event :int) -> void:
+ if event == NOTIFICATION_PREDELETE:
+ if _base != null:
+ _base.notification(event)
+ _base = null
+
+
+func _validate_value_type(value :Variant) -> bool:
+ return (
+ value == null
+ or typeof(value) in [
+ TYPE_VECTOR2,
+ TYPE_VECTOR2I,
+ TYPE_VECTOR3,
+ TYPE_VECTOR3I,
+ TYPE_VECTOR4,
+ TYPE_VECTOR4I
+ ]
+ )
+
+
+func _validate_is_vector_type(value :Variant) -> bool:
+ var type := typeof(value)
+ if type == _current_type or _current_type == TYPE_NIL:
+ return true
+ @warning_ignore("return_value_discarded")
+ report_error(GdAssertMessages.error_is_wrong_type(_current_type, type))
+ return false
+
+
+func current_value() -> Variant:
+ return _base.current_value()
+
+
+func report_success() -> GdUnitVectorAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_success()
+ return self
+
+
+func report_error(error :String) -> GdUnitVectorAssert:
+ @warning_ignore("return_value_discarded")
+ _base.report_error(error)
+ return self
+
+
+func failure_message() -> String:
+ return _base.failure_message()
+
+
+func override_failure_message(message: String) -> GdUnitVectorAssert:
+ @warning_ignore("return_value_discarded")
+ _base.override_failure_message(message)
+ return self
+
+
+func append_failure_message(message :String) -> GdUnitVectorAssert:
+ @warning_ignore("return_value_discarded")
+ _base.append_failure_message(message)
+ return self
+
+
+func is_null() -> GdUnitVectorAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_null()
+ return self
+
+
+func is_not_null() -> GdUnitVectorAssert:
+ @warning_ignore("return_value_discarded")
+ _base.is_not_null()
+ return self
+
+
+func is_equal(expected: Variant) -> GdUnitVectorAssert:
+ if _type_check and not _validate_is_vector_type(expected):
+ return self
+ @warning_ignore("return_value_discarded")
+ _base.is_equal(expected)
+ return self
+
+
+func is_not_equal(expected: Variant) -> GdUnitVectorAssert:
+ if _type_check and not _validate_is_vector_type(expected):
+ return self
+ @warning_ignore("return_value_discarded")
+ _base.is_not_equal(expected)
+ return self
+
+
+@warning_ignore("shadowed_global_identifier")
+func is_equal_approx(expected :Variant, approx :Variant) -> GdUnitVectorAssert:
+ if not _validate_is_vector_type(expected) or not _validate_is_vector_type(approx):
+ return self
+ var current :Variant = current_value()
+ var from :Variant = expected - approx
+ var to :Variant = expected + approx
+ if current == null or (not _is_equal_approx(current, from, to)):
+ return report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))
+ return report_success()
+
+
+func _is_equal_approx(current :Variant, from :Variant, to :Variant) -> bool:
+ match typeof(current):
+ TYPE_VECTOR2, TYPE_VECTOR2I:
+ return ((current.x >= from.x and current.y >= from.y)
+ and (current.x <= to.x and current.y <= to.y))
+ TYPE_VECTOR3, TYPE_VECTOR3I:
+ return ((current.x >= from.x and current.y >= from.y and current.z >= from.z)
+ and (current.x <= to.x and current.y <= to.y and current.z <= to.z))
+ TYPE_VECTOR4, TYPE_VECTOR4I:
+ return ((current.x >= from.x and current.y >= from.y and current.z >= from.z and current.w >= from.w)
+ and (current.x <= to.x and current.y <= to.y and current.z <= to.z and current.w <= to.w))
+ _:
+ push_error("Missing implementation '_is_equal_approx' for vector type %s" % typeof(current))
+ return false
+
+
+func is_less(expected :Variant) -> GdUnitVectorAssert:
+ if not _validate_is_vector_type(expected):
+ return self
+ var current :Variant = current_value()
+ if current == null or current >= expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.LESS_THAN, current, expected))
+ return report_success()
+
+
+func is_less_equal(expected :Variant) -> GdUnitVectorAssert:
+ if not _validate_is_vector_type(expected):
+ return self
+ var current :Variant = current_value()
+ if current == null or current > expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.LESS_EQUAL, current, expected))
+ return report_success()
+
+
+func is_greater(expected :Variant) -> GdUnitVectorAssert:
+ if not _validate_is_vector_type(expected):
+ return self
+ var current :Variant = current_value()
+ if current == null or current <= expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.GREATER_THAN, current, expected))
+ return report_success()
+
+
+func is_greater_equal(expected :Variant) -> GdUnitVectorAssert:
+ if not _validate_is_vector_type(expected):
+ return self
+ var current :Variant = current_value()
+ if current == null or current < expected:
+ return report_error(GdAssertMessages.error_is_value(Comparator.GREATER_EQUAL, current, expected))
+ return report_success()
+
+
+func is_between(from :Variant, to :Variant) -> GdUnitVectorAssert:
+ if not _validate_is_vector_type(from) or not _validate_is_vector_type(to):
+ return self
+ var current :Variant = current_value()
+ if current == null or not (current >= from and current <= to):
+ return report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))
+ return report_success()
+
+
+func is_not_between(from :Variant, to :Variant) -> GdUnitVectorAssert:
+ if not _validate_is_vector_type(from) or not _validate_is_vector_type(to):
+ return self
+ var current :Variant = current_value()
+ if (current != null and current >= from and current <= to):
+ return report_error(GdAssertMessages.error_is_value(Comparator.NOT_BETWEEN_EQUAL, current, from, to))
+ return report_success()
diff --git a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid
new file mode 100644
index 0000000..4f08785
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid
@@ -0,0 +1 @@
+uid://baa45s62kgmhi
diff --git a/addons/gdUnit4/src/asserts/ValueProvider.gd b/addons/gdUnit4/src/asserts/ValueProvider.gd
new file mode 100644
index 0000000..be01f70
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/ValueProvider.gd
@@ -0,0 +1,10 @@
+# base interface for assert value provider
+class_name ValueProvider
+extends RefCounted
+
+func get_value() -> Variant:
+ return null
+
+
+func dispose() -> void:
+ pass
diff --git a/addons/gdUnit4/src/asserts/ValueProvider.gd.uid b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid
new file mode 100644
index 0000000..f9e818d
--- /dev/null
+++ b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid
@@ -0,0 +1 @@
+uid://fh2vdv250867