Skip to content

Commit

Permalink
deps: update gdunit
Browse files Browse the repository at this point in the history
  • Loading branch information
russmatney committed Apr 14, 2024
1 parent 2bd6de4 commit 5719e6b
Show file tree
Hide file tree
Showing 29 changed files with 477 additions and 292 deletions.
70 changes: 48 additions & 22 deletions addons/gdUnit4/bin/GdUnitCmdTool.gd
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CLIRunner:
var _report: GdUnitHtmlReport
var _report_dir: String
var _report_max: int = DEFAULT_REPORT_COUNT
var _headless_mode_ignore := false
var _runner_config := GdUnitRunnerConfig.new()
var _console := CmdConsole.new()
var _cmd_options := CmdOptions.new([
Expand Down Expand Up @@ -91,7 +92,12 @@ class CLIRunner:
CmdOption.new(
"--selftest", "",
"Runs the GdUnit self test"
)
),
CmdOption.new(
"--ignoreHeadlessMode",
"--ignoreHeadlessMode",
"By default, running GdUnit4 in headless mode is not allowed. You can switch off the headless mode check by set this property."
),
])


Expand All @@ -102,7 +108,7 @@ class CLIRunner:
# stop checked first test failure to fail fast
_executor.fail_fast(true)
if GdUnit4CSharpApiLoader.is_mono_supported():
prints("GdUnit4Mono Version %s loaded." % GdUnit4CSharpApiLoader.version())
prints("GdUnit4Net version '%s' loaded." % GdUnit4CSharpApiLoader.version())
_cs_executor = GdUnit4CSharpApiLoader.create_executor(self)
var err := GdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)
if err != OK:
Expand Down Expand Up @@ -142,6 +148,7 @@ class CLIRunner:


func quit(code: int) -> void:
_cs_executor = null
GdUnitTools.dispose_all()
await GdUnitMemoryObserver.gc_on_guarded_instances()
await get_tree().physics_frame
Expand Down Expand Up @@ -203,6 +210,10 @@ class CLIRunner:
quit(RETURN_SUCCESS)


func check_headless_mode() -> void:
_headless_mode_ignore = true


func show_options(show_advanced: bool = false) -> void:
_console.prints_color(
"""
Expand Down Expand Up @@ -270,20 +281,6 @@ class CLIRunner:
Color.DARK_SALMON
).new_line()

if DisplayServer.get_name() == "headless":
_console.prints_error(
"Headless mode is not supported!"
).print_color("""
Tests that use UI interaction do not work in headless mode because 'InputEvents' are not transported
by the Godot engine and thus have no effect!
""".dedent(),
Color.CORNFLOWER_BLUE
).prints_error(
"Abnormal exit with %d" % RETURN_ERROR_HEADLESS_NOT_SUPPORTED
)
quit(RETURN_ERROR_HEADLESS_NOT_SUPPORTED)
return

var cmd_parser := CmdArgumentParser.new(_cmd_options, "GdUnitCmdTool.gd")
var result := cmd_parser.parse(OS.get_cmdline_args())
if result.is_error():
Expand All @@ -305,19 +302,46 @@ class CLIRunner:
.register_cbv("-a", Callable(_runner_config, "add_test_suites"))
.register_cb("-i", Callable(_runner_config, "skip_test_suite"))
.register_cbv("-i", Callable(_runner_config, "skip_test_suites"))
.register_cb("-rd", Callable(self, "set_report_dir"))
.register_cb("-rc", Callable(self, "set_report_count"))
.register_cb("--selftest", Callable(self, "run_self_test"))
.register_cb("-c", Callable(self, "disable_fail_fast"))
.register_cb("-conf", Callable(self, "load_test_config"))
.register_cb("--info", Callable(self, "show_version"))
.register_cb("-rd", set_report_dir)
.register_cb("-rc", set_report_count)
.register_cb("--selftest", run_self_test)
.register_cb("-c", disable_fail_fast)
.register_cb("-conf", load_test_config)
.register_cb("--info", show_version)
.register_cb("--ignoreHeadlessMode", check_headless_mode)
.execute(result.value())
)
if result.is_error():
_console.prints_error(result.error_message())
_state = STOP
quit(RETURN_ERROR)

if DisplayServer.get_name() == "headless":
if _headless_mode_ignore:
_console.prints_warning("""
Headless mode is ignored by option '--ignoreHeadlessMode'"
Please note that tests that use UI interaction do not work correctly in headless mode.
Godot 'InputEvents' are not transported by the Godot engine in headless mode and therefore
have no effect in the test!
""".dedent()
).new_line()
else:
_console.prints_error("""
Headless mode is not supported!
Please note that tests that use UI interaction do not work correctly in headless mode.
Godot 'InputEvents' are not transported by the Godot engine in headless mode and therefore
have no effect in the test!
You can run with '--ignoreHeadlessMode' to swtich off this check.
""".dedent()
).prints_error(
"Abnormal exit with %d" % RETURN_ERROR_HEADLESS_NOT_SUPPORTED
)
quit(RETURN_ERROR_HEADLESS_NOT_SUPPORTED)
return

_test_suites_to_process = load_testsuites(_runner_config)
if _test_suites_to_process.is_empty():
_console.prints_warning("No test suites found, abort test run!")
Expand Down Expand Up @@ -412,6 +436,8 @@ class CLIRunner:
GdUnitEvent.INIT:
_report = GdUnitHtmlReport.new(_report_dir)
GdUnitEvent.STOP:
if _report == null:
_report = GdUnitHtmlReport.new(_report_dir)
var report_path := _report.write()
_report.delete_history(_report_max)
JUnitXmlReport.new(_report._report_path, _report.iteration()).write(_report)
Expand Down
2 changes: 1 addition & 1 deletion addons/gdUnit4/plugin.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
name="gdUnit4"
description="Unit Testing Framework for Godot Scripts"
author="Mike Schulze"
version="4.2.3"
version="4.2.4"
script="plugin.gd"
2 changes: 1 addition & 1 deletion addons/gdUnit4/plugin.gd
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func _enter_tree() -> void:
var update_tool :Node = load("res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn").instantiate()
Engine.get_main_loop().root.call_deferred("add_child", update_tool)
if GdUnit4CSharpApiLoader.is_mono_supported():
prints("GdUnit4Mono Version %s loaded." % GdUnit4CSharpApiLoader.version())
prints("GdUnit4Net version '%s' loaded." % GdUnit4CSharpApiLoader.version())


func _exit_tree() -> void:
Expand Down
4 changes: 2 additions & 2 deletions addons/gdUnit4/runtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ if [ -z "$GODOT_BIN" ]; then
exit 1
fi

$GODOT_BIN --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $*
"$GODOT_BIN" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $*
exit_code=$?
echo "Run tests ends with $exit_code"

$GODOT_BIN --headless --path . --quiet -s -d res://addons/gdUnit4/bin/GdUnitCopyLog.gd $* > /dev/null
"$GODOT_BIN" --headless --path . --quiet -s -d res://addons/gdUnit4/bin/GdUnitCopyLog.gd $* > /dev/null
exit_code2=$?
exit $exit_code
5 changes: 4 additions & 1 deletion addons/gdUnit4/src/Fuzzers.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ extends Resource


## Generates an random string with min/max length and given charset
static func rand_str(min_length :int, max_length, charset := StringFuzzer.DEFAULT_CHARSET) -> Fuzzer:
static func rand_str(min_length: int, max_length, charset := StringFuzzer.DEFAULT_CHARSET) -> Fuzzer:
return StringFuzzer.new(min_length, max_length, charset)


## Generates an random integer in a range form to
static func rangei(from: int, to: int) -> Fuzzer:
return IntFuzzer.new(from, to)

## Generates a randon float within in a given range
static func rangef(from: float, to: float) -> Fuzzer:
return FloatFuzzer.new(from, to)

## Generates an random Vector2 in a range form to
static func rangev2(from: Vector2, to: Vector2) -> Fuzzer:
Expand Down
9 changes: 4 additions & 5 deletions addons/gdUnit4/src/GdUnitAwaiter.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@ const GdUnitAssertImpl = preload("res://addons/gdUnit4/src/asserts/GdUnitAssertI
# timeout: the timeout in ms, default is set to 2000ms
func await_signal_on(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:
# fail fast if the given source instance invalid
var assert_that := GdUnitAssertImpl.new(signal_name)
var line_number := GdUnitAssertions.get_line_number()
if not is_instance_valid(source):
GdUnitAssertImpl.new(signal_name)\
.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)
assert_that.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)
return await Engine.get_main_loop().process_frame
# fail fast if the given source instance invalid
if not is_instance_valid(source):
GdUnitAssertImpl.new(signal_name)\
.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)
assert_that.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)
return await await_idle_frame()
var awaiter := GdUnitSignalAwaiter.new(timeout_millis)
var value :Variant = await awaiter.on_signal(source, signal_name, args)
if awaiter.is_interrupted():
var failure = "await_signal_on(%s, %s) timed out after %sms" % [signal_name, args, timeout_millis]
GdUnitAssertImpl.new(signal_name).report_error(failure, line_number)
assert_that.report_error(failure, line_number)
return value


Expand Down
2 changes: 2 additions & 0 deletions addons/gdUnit4/src/GdUnitConstants.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ class_name GdUnitConstants
extends RefCounted

const NO_ARG :Variant = "<--null-->"

const EXPECT_ASSERT_REPORT_FAILURES := "expect_assert_report_failures"
12 changes: 4 additions & 8 deletions addons/gdUnit4/src/asserts/GdAssertReports.gd
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static func report_error(message:String, line_number :int) -> void:
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 is_expect_fail():
if _do_expect_assert_failing():
return
send_report(GdUnitReport.new().create(GdUnitReport.FAILURE, line_number, message))

Expand All @@ -40,13 +40,9 @@ static func get_last_error_line_number() -> int:
return -1


static func expect_fail(enabled :bool = true):
Engine.set_meta("report_failures", enabled)


static func is_expect_fail() -> bool:
if Engine.has_meta("report_failures"):
return Engine.get_meta("report_failures")
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


Expand Down
13 changes: 9 additions & 4 deletions addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ var _is_failed := false
var _failure_message :String


static func _set_do_expect_fail(enabled :bool = true):
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
GdAssertReports.expect_fail(true)
_set_do_expect_fail(true)
var thread_context := GdUnitThreadManager.get_current_context()
thread_context.set_assert(null)
GdUnitSignals.instance().gdunit_set_test_failed.connect(_on_test_failed)
Expand All @@ -17,13 +21,13 @@ func execute_and_await(assertion :Callable, do_await := true) -> GdUnitFailureAs
await assertion.call()
else:
assertion.call()
GdAssertReports.expect_fail(false)
_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
return self
_failure_message = current_assert.failure_message()
return self

Expand Down Expand Up @@ -75,7 +79,8 @@ func has_line(expected :int) -> GdUnitFailureAssert:


func has_message(expected :String) -> GdUnitFailureAssert:
var expected_error := GdUnitTools.normalize_text(expected)
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)
Expand Down
7 changes: 6 additions & 1 deletion addons/gdUnit4/src/cmd/CmdCommandHandler.gd
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,15 @@ func execute(commands :Array) -> GdUnitResult:
if _command_cbs.has(cmd_name):
var cb_s :Callable = _command_cbs.get(cmd_name)[CB_SINGLE_ARG]
var arguments := cmd.arguments()
var cmd_option := _cmd_options.get_option(cmd_name)
var argument = arguments[0] if arguments.size() > 0 else null
match cmd_option.type():
TYPE_BOOL:
argument = true if argument == "true" else false
if cb_s and arguments.size() == 0:
cb_s.call()
elif cb_s:
cb_s.call(arguments[0])
cb_s.call(argument)
else:
var cb_m :Callable = _command_cbs.get(cmd_name)[CB_MULTI_ARGS]
# we need to find the method and determin the arguments to call the right function
Expand Down
14 changes: 10 additions & 4 deletions addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ func scan(resource_path :String) -> Array[Node]:
# if single testsuite requested
if FileAccess.file_exists(resource_path):
var test_suite := _parse_is_test_suite(resource_path)
if test_suite:
if test_suite != null:
return [test_suite]
return [] as Array[Node]
var base_dir := DirAccess.open(resource_path)
if base_dir == null:
prints("Given directory or file does not exists:", resource_path)
Expand Down Expand Up @@ -97,10 +98,15 @@ static func _is_script_format_supported(resource_path :String) -> bool:


func _parse_test_suite(script :GDScript) -> GdUnitTestSuite:
# find all test cases
var test_case_names := _extract_test_case_names(script)
# test suite do not contains any tests
if test_case_names.is_empty():
push_warning("The test suite %s do not contain any tests, it excludes from discovery." % script.resource_path)
return null;

var test_suite = script.new()
test_suite.set_name(GdUnitTestSuiteScanner.parse_test_suite_name(script))
# find all test cases as array of names
var test_case_names := _extract_test_case_names(script)
# add test cases to test suite and parse test case line nummber
_parse_and_add_test_cases(test_suite, script, test_case_names)
# not all test case parsed?
Expand Down Expand Up @@ -183,7 +189,7 @@ func _handle_test_case_arguments(test_suite, script :GDScript, fd :GdFunctionDes

func _parse_and_add_test_cases(test_suite, script :GDScript, test_case_names :PackedStringArray):
var test_cases_to_find = Array(test_case_names)
var functions_to_scan := test_case_names
var functions_to_scan := test_case_names.duplicate()
functions_to_scan.append("before")
var source := _script_parser.load_source_code(script, [script.resource_path])
var function_descriptors := _script_parser.parse_functions(source, "", [script.resource_path], functions_to_scan)
Expand Down
4 changes: 4 additions & 0 deletions addons/gdUnit4/src/core/parse/GdScriptParser.gd
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ var TOKENS := [
]

var _regex_clazz_name :RegEx
var _regex_strip_comments := GdUnitTools.to_regex("^([^#\"']|'[^']*'|\"[^\"]*\")*\\K#.*")
var _base_clazz :String
var _scanned_inner_classes := PackedStringArray()
var _script_constants := {}
Expand Down Expand Up @@ -615,6 +616,9 @@ func extract_func_signature(rows :PackedStringArray, index :int) -> String:

for rowIndex in range(index, rows.size()):
var row := rows[rowIndex]
row = _regex_strip_comments.sub(row, "").strip_edges(false)
if row.is_empty():
continue
signature += row + "\n"
if is_func_end(row):
return signature.strip_edges()
Expand Down
13 changes: 13 additions & 0 deletions addons/gdUnit4/src/fuzzers/FloatFuzzer.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class_name FloatFuzzer
extends Fuzzer

var _from: float = 0
var _to: float = 0

func _init(from: float, to: float):
assert(from <= to, "Invalid range!")
_from = from
_to = to

func next_value() -> Variant:
return randf_range(_from, _to)
Loading

0 comments on commit 5719e6b

Please sign in to comment.