From 5719e6b667d49567668d4744a1d1d2ae990dd6c1 Mon Sep 17 00:00:00 2001 From: Russell Matney Date: Sat, 13 Apr 2024 23:54:24 -0400 Subject: [PATCH] deps: update gdunit --- addons/gdUnit4/bin/GdUnitCmdTool.gd | 70 ++++++---- addons/gdUnit4/plugin.cfg | 2 +- addons/gdUnit4/plugin.gd | 2 +- addons/gdUnit4/runtest.sh | 4 +- addons/gdUnit4/src/Fuzzers.gd | 5 +- addons/gdUnit4/src/GdUnitAwaiter.gd | 9 +- addons/gdUnit4/src/GdUnitConstants.gd | 2 + addons/gdUnit4/src/asserts/GdAssertReports.gd | 12 +- .../src/asserts/GdUnitFailureAssertImpl.gd | 13 +- addons/gdUnit4/src/cmd/CmdCommandHandler.gd | 7 +- .../src/core/GdUnitTestSuiteScanner.gd | 14 +- .../gdUnit4/src/core/parse/GdScriptParser.gd | 4 + addons/gdUnit4/src/fuzzers/FloatFuzzer.gd | 13 ++ addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs | 51 ++++++-- addons/gdUnit4/test/GdUnitAwaiterTest.gd | 40 +++--- .../test/asserts/GdUnitFuncAssertImplTest.gd | 74 +++++------ .../asserts/GdUnitSignalAssertImplTest.gd | 65 +++++----- .../test/core/GdUnitSceneRunnerTest.gd | 35 ++--- .../test/core/GdUnitSignalAwaiterTest.gd | 6 +- .../test/core/GdUnitTestSuiteScannerTest.gd | 47 +++---- .../test/core/parse/GdScriptParserTest.gd | 122 +++++++++++------- .../GdUnitTestParameterSetResolverTest.gd | 109 +++++++++++++--- .../by_class_name/BaseTest.gd | 17 +++ .../by_class_name/ExtendedTest.gd | 9 ++ .../testsuites/TestSuiteWithoutTests.gd | 9 ++ .../resources/ClassWithCustomFormattings.gd | 8 +- .../gdUnit4/test/mono/GdUnit4CSharpApiTest.cs | 2 +- addons/log/plugin.gd | 16 --- project.godot | 2 +- 29 files changed, 477 insertions(+), 292 deletions(-) create mode 100644 addons/gdUnit4/src/fuzzers/FloatFuzzer.gd create mode 100644 addons/gdUnit4/test/core/resources/testsuites/TestSuiteWithoutTests.gd diff --git a/addons/gdUnit4/bin/GdUnitCmdTool.gd b/addons/gdUnit4/bin/GdUnitCmdTool.gd index 00d5ad9..677ed99 100644 --- a/addons/gdUnit4/bin/GdUnitCmdTool.gd +++ b/addons/gdUnit4/bin/GdUnitCmdTool.gd @@ -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([ @@ -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." + ), ]) @@ -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: @@ -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 @@ -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( """ @@ -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(): @@ -305,12 +302,13 @@ 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(): @@ -318,6 +316,32 @@ class CLIRunner: _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!") @@ -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) diff --git a/addons/gdUnit4/plugin.cfg b/addons/gdUnit4/plugin.cfg index a264864..fce580f 100644 --- a/addons/gdUnit4/plugin.cfg +++ b/addons/gdUnit4/plugin.cfg @@ -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" diff --git a/addons/gdUnit4/plugin.gd b/addons/gdUnit4/plugin.gd index 963673e..8aedfc0 100644 --- a/addons/gdUnit4/plugin.gd +++ b/addons/gdUnit4/plugin.gd @@ -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: diff --git a/addons/gdUnit4/runtest.sh b/addons/gdUnit4/runtest.sh index 83eccc2..83ed272 100755 --- a/addons/gdUnit4/runtest.sh +++ b/addons/gdUnit4/runtest.sh @@ -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 diff --git a/addons/gdUnit4/src/Fuzzers.gd b/addons/gdUnit4/src/Fuzzers.gd index dfed451..8affdbf 100644 --- a/addons/gdUnit4/src/Fuzzers.gd +++ b/addons/gdUnit4/src/Fuzzers.gd @@ -4,7 +4,7 @@ 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) @@ -12,6 +12,9 @@ static func rand_str(min_length :int, max_length, charset := StringFuzzer.DEFAUL 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: diff --git a/addons/gdUnit4/src/GdUnitAwaiter.gd b/addons/gdUnit4/src/GdUnitAwaiter.gd index cc6bf3f..a9b5bb1 100644 --- a/addons/gdUnit4/src/GdUnitAwaiter.gd +++ b/addons/gdUnit4/src/GdUnitAwaiter.gd @@ -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 diff --git a/addons/gdUnit4/src/GdUnitConstants.gd b/addons/gdUnit4/src/GdUnitConstants.gd index 578d032..0445894 100644 --- a/addons/gdUnit4/src/GdUnitConstants.gd +++ b/addons/gdUnit4/src/GdUnitConstants.gd @@ -2,3 +2,5 @@ class_name GdUnitConstants extends RefCounted const NO_ARG :Variant = "<--null-->" + +const EXPECT_ASSERT_REPORT_FAILURES := "expect_assert_report_failures" diff --git a/addons/gdUnit4/src/asserts/GdAssertReports.gd b/addons/gdUnit4/src/asserts/GdAssertReports.gd index dc427ed..1ac9e04 100644 --- a/addons/gdUnit4/src/asserts/GdAssertReports.gd +++ b/addons/gdUnit4/src/asserts/GdAssertReports.gd @@ -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)) @@ -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 diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd index 6b1cdaa..bcb949f 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd @@ -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) @@ -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 @@ -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) diff --git a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd index 18fe542..3cc10cd 100644 --- a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd +++ b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd @@ -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 diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd index 56e963d..af933b2 100644 --- a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd +++ b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd @@ -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) @@ -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? @@ -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) diff --git a/addons/gdUnit4/src/core/parse/GdScriptParser.gd b/addons/gdUnit4/src/core/parse/GdScriptParser.gd index dea681f..37e0067 100644 --- a/addons/gdUnit4/src/core/parse/GdScriptParser.gd +++ b/addons/gdUnit4/src/core/parse/GdScriptParser.gd @@ -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 := {} @@ -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() diff --git a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd new file mode 100644 index 0000000..6a87c37 --- /dev/null +++ b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd @@ -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) diff --git a/addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs b/addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs index 6c27132..ec07ff4 100644 --- a/addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs +++ b/addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs @@ -1,19 +1,50 @@ +using System; +using System.Reflection; +using System.Linq; + using Godot; using Godot.Collections; - using GdUnit4; + // GdUnit4 GDScript - C# API wrapper public partial class GdUnit4CSharpApi : RefCounted { - public static string Version() => GdUnit4MonoAPI.Version(); - - public static bool IsTestSuite(string classPath) => GdUnit4MonoAPI.IsTestSuite(classPath); - - public static RefCounted Executor(Node listener) => (RefCounted)GdUnit4MonoAPI.Executor(listener); - - public static GdUnit4.CsNode? ParseTestSuite(string classPath) => GdUnit4MonoAPI.ParseTestSuite(classPath); - + private static Type? apiType; + + private static Type GetApiType() + { + if (apiType == null) + { + var assembly = Assembly.Load("gdUnit4Api"); + apiType = GdUnit4NetVersion() < new Version(4, 2, 2) ? + assembly.GetType("GdUnit4.GdUnit4MonoAPI") : + assembly.GetType("GdUnit4.GdUnit4NetAPI"); + Godot.GD.PrintS($"GdUnit4CSharpApi type:{apiType} loaded."); + } + return apiType!; + } + + private static Version GdUnit4NetVersion() + { + var assembly = Assembly.Load("gdUnit4Api"); + return assembly.GetName().Version!; + } + + private static T InvokeApiMethod(string methodName, params object[] args) + { + var method = GetApiType().GetMethod(methodName)!; + return (T)method.Invoke(null, args)!; + } + + public static string Version() => GdUnit4NetVersion().ToString(); + + public static bool IsTestSuite(string classPath) => InvokeApiMethod("IsTestSuite", classPath); + + public static RefCounted Executor(Node listener) => InvokeApiMethod("Executor", listener); + + public static CsNode? ParseTestSuite(string classPath) => InvokeApiMethod("ParseTestSuite", classPath); + public static Dictionary CreateTestSuite(string sourcePath, int lineNumber, string testSuitePath) => - GdUnit4MonoAPI.CreateTestSuite(sourcePath, lineNumber, testSuitePath); + InvokeApiMethod("CreateTestSuite", sourcePath, lineNumber, testSuitePath); } diff --git a/addons/gdUnit4/test/GdUnitAwaiterTest.gd b/addons/gdUnit4/test/GdUnitAwaiterTest.gd index 26ffccb..54f0e3a 100644 --- a/addons/gdUnit4/test/GdUnitAwaiterTest.gd +++ b/addons/gdUnit4/test/GdUnitAwaiterTest.gd @@ -21,16 +21,6 @@ func after_test(): node.free() -## Utility to check if a test has failed in a particular line and if there is an error message -func assert_failed_at(line_number :int, expected_failure :String) -> bool: - var is_failed = is_failure() - var last_failure = GdAssertReports.current_failure() - var last_failure_line = GdAssertReports.get_last_error_line_number() - assert_str(last_failure).is_equal(expected_failure) - assert_int(last_failure_line).is_equal(line_number) - return is_failed - - func install_signal_emitter(signal_name :String, signal_args: Array = [], time_out : float = 0.020): var timer := Timer.new() add_child(timer) @@ -38,6 +28,7 @@ func install_signal_emitter(signal_name :String, signal_args: Array = [], time_o timer.one_shot = true timer.start(time_out) + func emit_test_signal(signal_name :String, signal_args: Array): match signal_args.size(): 0: emit_signal(signal_name) @@ -45,6 +36,7 @@ func emit_test_signal(signal_name :String, signal_args: Array): 2: emit_signal(signal_name, signal_args[0], signal_args[1]) 3: emit_signal(signal_name, signal_args[0], signal_args[1], signal_args[2]) + func test_await_signal_on() -> void: install_signal_emitter("test_signal_a") await await_signal_on(self, "test_signal_a", [], 100) @@ -61,6 +53,7 @@ func test_await_signal_on() -> void: install_signal_emitter("test_signal_c", ["abc", "eee"]) await await_signal_on(self, "test_signal_c", ["abc", "eee"], 100) + func test_await_signal_on_manysignals_emitted() -> void: # emits many different signals install_signal_emitter("test_signal_a") @@ -73,24 +66,21 @@ func test_await_signal_on_manysignals_emitted() -> void: # we only wait for 'test_signal_c("abc")' is emitted await await_signal_on(self, "test_signal_c", ["abc"], 300) + func test_await_signal_on_never_emitted_timedout() -> void: - # we expect 'await_signal_on' will fail, do not report as failure - GdAssertReports.expect_fail() - # we wait for 'test_signal_c("yyy")' which is never emitted - await await_signal_on(self, "test_signal_c", ["yyy"], 200) - # expect is failed by a timeout at line 68 - if assert_failed_at(68, "await_signal_on(test_signal_c, [\"yyy\"]) timed out after 200ms"): - return - fail("test should failed after 400ms on 'await_signal_on'") + ( + # we wait for 'test_signal_c("yyy")' which is never emitted + await assert_failure_await(func x(): await await_signal_on(self, "test_signal_c", ["yyy"], 200)) + ).has_line(73)\ + .has_message("await_signal_on(test_signal_c, [\"yyy\"]) timed out after 200ms") + func test_await_signal_on_invalid_source_timedout() -> void: - # we expect 'await_signal_on' will fail, do not report as failure - GdAssertReports.expect_fail() - # we wait for a signal on a already freed instance - await await_signal_on(invalid_node(), "tree_entered", [], 300) - if assert_failed_at(78, GdAssertMessages.error_await_signal_on_invalid_instance(null, "tree_entered", [])): - return - fail("test should failed after 400ms on 'await_signal_on'") + ( + # we wait for a signal on a already freed instance + await assert_failure_await(func x(): await await_signal_on(invalid_node(), "tree_entered", [], 300)) + ).has_line(81).has_message(GdAssertMessages.error_await_signal_on_invalid_instance(null, "tree_entered", [])) + func invalid_node() -> Node: return null diff --git a/addons/gdUnit4/test/asserts/GdUnitFuncAssertImplTest.gd b/addons/gdUnit4/test/asserts/GdUnitFuncAssertImplTest.gd index f57a0ba..8678a6f 100644 --- a/addons/gdUnit4/test/asserts/GdUnitFuncAssertImplTest.gd +++ b/addons/gdUnit4/test/asserts/GdUnitFuncAssertImplTest.gd @@ -14,16 +14,6 @@ func is_skip_fail_await() -> bool: return Engine.get_version_info().hex < 0x40002 -# using this helper to await for the given callable and assert the failure -func verify_failed(cb :Callable) -> GdUnitStringAssert: - GdAssertReports.expect_fail(true) - await cb.call() - GdAssertReports.expect_fail(false) - - var error = GdAssertReports.current_failure() - return assert_str(GdUnitTools.richtext_normalize(error)) - - class TestValueProvider: var _max_iterations :int var _current_itteration := 0 @@ -149,8 +139,9 @@ func test_is_null(timeout = 2000) -> void: if is_skip_fail_await(): return value_provider = TestIterativeValueProvider.new(RefCounted.new(), 1, RefCounted.new()) - (await verify_failed(func(): await assert_func(value_provider, "obj_value", []).wait_until(100).is_null())) \ - .is_equal("Expected: is null but timed out after 100ms") + ( + await assert_failure_await(func(): await assert_func(value_provider, "obj_value", []).wait_until(100).is_null()) + ).has_message("Expected: is null but timed out after 100ms") @warning_ignore("unused_parameter") @@ -171,8 +162,9 @@ func test_is_not_null(timeout = 2000) -> void: value_provider = TestIterativeValueProvider.new(null, 1, null) if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(value_provider, "obj_value", []).wait_until(100).is_not_null()))\ - .is_equal("Expected: is not null but timed out after 100ms") + ( + await assert_failure_await(func(): await assert_func(value_provider, "obj_value", []).wait_until(100).is_not_null()) + ).has_message("Expected: is not null but timed out after 100ms") @warning_ignore("unused_parameter") @@ -193,8 +185,9 @@ func test_is_true(timeout = 2000) -> void: value_provider = TestIterativeValueProvider.new(false, 1, false) if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(value_provider, "bool_value", []).wait_until(100).is_true()))\ - .is_equal("Expected: is true but timed out after 100ms") + ( + await assert_failure_await(func(): await assert_func(value_provider, "bool_value", []).wait_until(100).is_true()) + ).has_message("Expected: is true but timed out after 100ms") @warning_ignore("unused_parameter") @@ -215,8 +208,9 @@ func test_is_false(timeout = 2000) -> void: value_provider = TestIterativeValueProvider.new(true, 1, true) if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(value_provider, "bool_value", []).wait_until(100).is_false())) \ - .is_equal("Expected: is false but timed out after 100ms") + ( + await assert_failure_await(func(): await assert_func(value_provider, "bool_value", []).wait_until(100).is_false()) + ).has_message("Expected: is false but timed out after 100ms") @warning_ignore("unused_parameter") @@ -237,8 +231,9 @@ func test_is_equal(timeout = 2000) -> void: value_provider = TestIterativeValueProvider.new(23, 1, 23) if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(value_provider, "int_value", []).wait_until(100).is_equal(25))) \ - .is_equal("Expected: is equal '25' but timed out after 100ms") + ( + await assert_failure_await(func(): await assert_func(value_provider, "int_value", []).wait_until(100).is_equal(25)) + ).has_message("Expected: is equal '25' but timed out after 100ms") @warning_ignore("unused_parameter") @@ -259,8 +254,9 @@ func test_is_not_equal(timeout = 2000) -> void: value_provider = TestIterativeValueProvider.new(23, 1, 23) if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(value_provider, "int_value", []).wait_until(100).is_not_equal(23))) \ - .is_equal("Expected: is not equal '23' but timed out after 100ms") + ( + await assert_failure_await(func(): await assert_func(value_provider, "int_value", []).wait_until(100).is_not_equal(23)) + ).has_message("Expected: is not equal '23' but timed out after 100ms") @warning_ignore("unused_parameter") @@ -307,15 +303,17 @@ func test_timer_yielded_function() -> void: # failure case if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(self, "timed_function", []).wait_until(100).is_equal(Color.RED))) \ - .is_equal("Expected: is equal 'Color(1, 0, 0, 1)' but timed out after 100ms") + ( + await assert_failure_await(func(): await assert_func(self, "timed_function", []).wait_until(100).is_equal(Color.RED)) + ).has_message("Expected: is equal 'Color(1, 0, 0, 1)' but timed out after 100ms") func test_timer_yielded_function_timeout() -> void: if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(self, "timed_function", []).wait_until(40).is_equal(Color.BLACK)))\ - .is_equal("Expected: is equal 'Color()' but timed out after 40ms") + ( + await assert_failure_await(func(): await assert_func(self, "timed_function", []).wait_until(40).is_equal(Color.BLACK)) + ).has_message("Expected: is equal 'Color()' but timed out after 40ms") func yielded_function() -> Color: @@ -333,34 +331,38 @@ func test_idle_frame_yielded_function() -> void: await assert_func(self, "yielded_function").is_equal(Color.BLACK) if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(self, "yielded_function", []).wait_until(500).is_equal(Color.RED))) \ - .is_equal("Expected: is equal 'Color(1, 0, 0, 1)' but timed out after 500ms") + ( + await assert_failure_await(func(): await assert_func(self, "yielded_function", []).wait_until(500).is_equal(Color.RED)) + ).has_message("Expected: is equal 'Color(1, 0, 0, 1)' but timed out after 500ms") func test_has_failure_message() -> void: if is_skip_fail_await(): return var value_provider := TestIterativeValueProvider.new(10, 1, 10) - (await verify_failed(func(): await assert_func(value_provider, "int_value", []).wait_until(500).is_equal(42))) \ - .is_equal("Expected: is equal '42' but timed out after 500ms") + ( + await assert_failure_await(func(): await assert_func(value_provider, "int_value", []).wait_until(500).is_equal(42)) + ).has_message("Expected: is equal '42' but timed out after 500ms") func test_override_failure_message() -> void: if is_skip_fail_await(): return var value_provider := TestIterativeValueProvider.new(10, 1, 20) - (await verify_failed(func(): await assert_func(value_provider, "int_value", []) \ + ( + await assert_failure_await(func(): await assert_func(value_provider, "int_value", []) \ .override_failure_message("Custom failure message") \ .wait_until(100) \ - .is_equal(42))) \ - .is_equal("Custom failure message") + .is_equal(42)) + ).has_message("Custom failure message") @warning_ignore("unused_parameter") func test_invalid_function(timeout = 100): if is_skip_fail_await(): return - (await verify_failed(func(): await assert_func(self, "invalid_func_name", [])\ + ( + await assert_failure_await(func(): await assert_func(self, "invalid_func_name", [])\ .wait_until(1000)\ - .is_equal(42)))\ - .starts_with("The function 'invalid_func_name' do not exists checked instance") + .is_equal(42)) + ).starts_with_message("The function 'invalid_func_name' do not exists checked instance") diff --git a/addons/gdUnit4/test/asserts/GdUnitSignalAssertImplTest.gd b/addons/gdUnit4/test/asserts/GdUnitSignalAssertImplTest.gd index 0c1d183..1c635c6 100644 --- a/addons/gdUnit4/test/asserts/GdUnitSignalAssertImplTest.gd +++ b/addons/gdUnit4/test/asserts/GdUnitSignalAssertImplTest.gd @@ -46,26 +46,19 @@ func is_skip_fail_await() -> bool: return Engine.get_version_info().hex < 0x40002 -# using this helper to await for the given callable and assert the failure -func verify_failed(cb :Callable) -> GdUnitStringAssert: - GdAssertReports.expect_fail(true) - await cb.call() - GdAssertReports.expect_fail(false) - - var a :GdUnitSignalAssert = GdUnitThreadManager.get_current_context().get_assert() - return assert_str(GdUnitTools.richtext_normalize(a.failure_message())) - - func test_invalid_arg() -> void: - (await verify_failed(func(): assert_signal(null).wait_until(50).is_emitted("test_signal_counted")))\ - .is_equal("Can't wait for signal checked a NULL object.") - (await verify_failed(func(): await assert_signal(null).wait_until(50).is_not_emitted("test_signal_counted")))\ - .is_equal("Can't wait for signal checked a NULL object.") + ( + await assert_failure_await(func(): await assert_signal(null).wait_until(50).is_emitted("test_signal_counted")) + ).has_message("Can't wait for signal checked a NULL object.") + ( + await assert_failure_await(func(): await assert_signal(null).wait_until(50).is_not_emitted("test_signal_counted")) + ).has_message("Can't wait for signal checked a NULL object.") func test_unknown_signal() -> void: - (await verify_failed(func(): await assert_signal(signal_emitter).wait_until(50).is_emitted("unknown"))) \ - .is_equal("Can't wait for non-existion signal 'unknown' checked object 'Node'.") + ( + await assert_failure_await(func(): await assert_signal(signal_emitter).wait_until(50).is_emitted("unknown")) + ).has_message("Can't wait for non-existion signal 'unknown' checked object 'Node'.") func test_signal_is_emitted_without_args() -> void: @@ -76,9 +69,9 @@ func test_signal_is_emitted_without_args() -> void: if is_skip_fail_await(): return - - (await verify_failed(func(): await assert_signal(signal_emitter).wait_until(500).is_emitted("test_signal_unused")))\ - .is_equal("Expecting emit signal: 'test_signal_unused()' but timed out after 500ms") + ( + await assert_failure_await(func(): await assert_signal(signal_emitter).wait_until(500).is_emitted("test_signal_unused")) + ).has_message("Expecting emit signal: 'test_signal_unused()' but timed out after 500ms") func test_signal_is_emitted_with_args() -> void: @@ -87,8 +80,9 @@ func test_signal_is_emitted_with_args() -> void: if is_skip_fail_await(): return - (await verify_failed(func(): await assert_signal(signal_emitter).wait_until(50).is_emitted("test_signal_counted", [500]))) \ - .is_equal("Expecting emit signal: 'test_signal_counted([500])' but timed out after 50ms") + ( + await assert_failure_await(func(): await assert_signal(signal_emitter).wait_until(50).is_emitted("test_signal_counted", [500])) + ).has_message("Expecting emit signal: 'test_signal_counted([500])' but timed out after 50ms") func test_signal_is_emitted_use_argument_matcher() -> void: @@ -101,10 +95,9 @@ func test_signal_is_emitted_use_argument_matcher() -> void: # should fail because the matcher uses the wrong type signal_emitter.reset_trigger() - (await verify_failed( func(): - await assert_signal(signal_emitter).wait_until(50).is_emitted("test_signal_counted", [any_string()]) - ) - ).is_equal("Expecting emit signal: 'test_signal_counted([any_string()])' but timed out after 50ms") + ( + await assert_failure_await( func(): await assert_signal(signal_emitter).wait_until(50).is_emitted("test_signal_counted", [any_string()])) + ).has_message("Expecting emit signal: 'test_signal_counted([any_string()])' but timed out after 50ms") func test_signal_is_not_emitted() -> void: @@ -116,19 +109,21 @@ func test_signal_is_not_emitted() -> void: if is_skip_fail_await(): return # until the next 500ms the signal is emitted and ends in a failure - (await verify_failed(func(): await assert_signal(signal_emitter).wait_until(1000).is_not_emitted("test_signal_counted", [50]))) \ - .starts_with("Expecting do not emit signal: 'test_signal_counted([50])' but is emitted after") + ( + await assert_failure_await(func(): await assert_signal(signal_emitter).wait_until(1000).is_not_emitted("test_signal_counted", [50])) + ).starts_with_message("Expecting do not emit signal: 'test_signal_counted([50])' but is emitted after") func test_override_failure_message() -> void: if is_skip_fail_await(): return - (await verify_failed(func(): await assert_signal(signal_emitter) \ + ( + await assert_failure_await(func(): await assert_signal(signal_emitter) \ .override_failure_message("Custom failure message")\ .wait_until(100)\ - .is_emitted("test_signal_unused"))) \ - .is_equal("Custom failure message") + .is_emitted("test_signal_unused")) + ).has_message("Custom failure message") func test_node_changed_emitting_signals(): @@ -143,8 +138,9 @@ func test_node_changed_emitting_signals(): # expecting to fail, we not changed the visibility #node.visible = true; if not is_skip_fail_await(): - (await verify_failed(func(): await assert_signal(node).wait_until(200).is_emitted("visibility_changed")))\ - .is_equal("Expecting emit signal: 'visibility_changed()' but timed out after 200ms") + ( + await assert_failure_await(func(): await assert_signal(node).wait_until(200).is_emitted("visibility_changed")) + ).has_message("Expecting emit signal: 'visibility_changed()' but timed out after 200ms") node.show() await assert_signal(node).wait_until(200).is_emitted("draw") @@ -163,8 +159,9 @@ func test_is_signal_exists() -> void: if is_skip_fail_await(): return - (await verify_failed(func(): assert_signal(node).is_signal_exists("not_existing_signal"))) \ - .is_equal("The signal 'not_existing_signal' not exists checked object 'Node2D'.") + ( + await assert_failure_await(func(): assert_signal(node).is_signal_exists("not_existing_signal")) + ).has_message("The signal 'not_existing_signal' not exists checked object 'Node2D'.") class MyEmitter extends Node: diff --git a/addons/gdUnit4/test/core/GdUnitSceneRunnerTest.gd b/addons/gdUnit4/test/core/GdUnitSceneRunnerTest.gd index 884a4da..c5edd60 100644 --- a/addons/gdUnit4/test/core/GdUnitSceneRunnerTest.gd +++ b/addons/gdUnit4/test/core/GdUnitSceneRunnerTest.gd @@ -20,17 +20,6 @@ func after(): Engine.set_max_fps(0) -## Utility to check if a test has failed in a particular line and if there is an error message -func assert_failed_at(line_number :int, expected_failure :String) -> bool: - var is_failed = is_failure() - var last_failure = GdAssertReports.current_failure() - var last_failure_line = GdAssertReports.get_last_error_line_number() - assert_str(last_failure).is_equal(expected_failure) - assert_int(last_failure_line).is_equal(line_number) - GdAssertReports.expect_fail(true) - return is_failed - - func test_get_property() -> void: var runner := scene_runner(load_test_scene()) @@ -211,13 +200,11 @@ func test_await_signal_without_time_factor() -> void: await runner.await_signal("panel_color_change", [box1, Color.RED]) await runner.await_signal("panel_color_change", [box1, Color.BLUE]) await runner.await_signal("panel_color_change", [box1, Color.GREEN]) - - # should be interrupted is will never change to Color.KHAKI - GdAssertReports.expect_fail() - await runner.await_signal( "panel_color_change", [box1, Color.KHAKI], 300) - if assert_failed_at(201, "await_signal_on(panel_color_change, [%s, %s]) timed out after 300ms" % [str(box1), str(Color.KHAKI)]): - return - fail("test should failed after 300ms checked 'await_signal'") + ( + # should be interrupted is will never change to Color.KHAKI + await assert_failure_await(func x(): await runner.await_signal( "panel_color_change", [box1, Color.KHAKI], 300)) + ).has_message("await_signal_on(panel_color_change, [%s, %s]) timed out after 300ms" % [str(box1), str(Color.KHAKI)])\ + .has_line(205) func test_await_signal_with_time_factor() -> void: @@ -230,13 +217,11 @@ func test_await_signal_with_time_factor() -> void: await runner.await_signal("panel_color_change", [box1, Color.RED], 100) await runner.await_signal("panel_color_change", [box1, Color.BLUE], 100) await runner.await_signal("panel_color_change", [box1, Color.GREEN], 100) - - # should be interrupted is will never change to Color.KHAKI - GdAssertReports.expect_fail() - await runner.await_signal("panel_color_change", [box1, Color.KHAKI], 30) - if assert_failed_at(220, "await_signal_on(panel_color_change, [%s, %s]) timed out after 30ms" % [str(box1), str(Color.KHAKI)]): - return - fail("test should failed after 30ms checked 'await_signal'") + ( + # should be interrupted is will never change to Color.KHAKI + await assert_failure_await(func x(): await runner.await_signal("panel_color_change", [box1, Color.KHAKI], 30)) + ).has_message("await_signal_on(panel_color_change, [%s, %s]) timed out after 30ms" % [str(box1), str(Color.KHAKI)])\ + .has_line(222) func test_simulate_until_signal() -> void: diff --git a/addons/gdUnit4/test/core/GdUnitSignalAwaiterTest.gd b/addons/gdUnit4/test/core/GdUnitSignalAwaiterTest.gd index 84ea862..f0d6a56 100644 --- a/addons/gdUnit4/test/core/GdUnitSignalAwaiterTest.gd +++ b/addons/gdUnit4/test/core/GdUnitSignalAwaiterTest.gd @@ -38,9 +38,9 @@ func test_on_signal_with_many_args() -> void: func test_on_signal_fail() -> void: - GdAssertReports.expect_fail() var monster = auto_free(Monster.new()) add_child(monster) - await await_signal_on(monster, "move", [4.0]) - assert_str(GdAssertReports.current_failure()).is_equal("await_signal_on(move, [4]) timed out after 2000ms") + ( + await assert_failure_await( func x(): await await_signal_on(monster, "move", [4.0])) + ).has_message("await_signal_on(move, [4]) timed out after 2000ms") remove_child(monster) diff --git a/addons/gdUnit4/test/core/GdUnitTestSuiteScannerTest.gd b/addons/gdUnit4/test/core/GdUnitTestSuiteScannerTest.gd index d9c4922..6ee5a4f 100644 --- a/addons/gdUnit4/test/core/GdUnitTestSuiteScannerTest.gd +++ b/addons/gdUnit4/test/core/GdUnitTestSuiteScannerTest.gd @@ -278,12 +278,22 @@ func test_scan_by_inheritance_class_name() -> void: var scanner :GdUnitTestSuiteScanner = auto_free(GdUnitTestSuiteScanner.new()) var test_suites := scanner.scan("res://addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/") - assert_array(test_suites).extractv(extr("get_name"), extr("get_script.get_path"), extr("get_children.get_name"))\ - .contains_exactly_in_any_order([ - tuple("BaseTest", "res://addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/BaseTest.gd", [&"test_foo1"]), - tuple("ExtendedTest","res://addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendedTest.gd", [&"test_foo2", &"test_foo1"]), - tuple("ExtendsExtendedTest", "res://addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendsExtendedTest.gd", [&"test_foo3", &"test_foo2", &"test_foo1"]) - ]) + assert_array(test_suites).has_size(3) + # sort by names + test_suites.sort_custom(func by_name(a, b): return a.get_name() <= b.get_name()) + assert_array(test_suites).extract("get_name")\ + .contains_exactly(["BaseTest", "ExtendedTest", "ExtendsExtendedTest"]) + assert_array(test_suites).extract("get_script.get_path")\ + .contains_exactly([ + "res://addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/BaseTest.gd", + "res://addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendedTest.gd", + "res://addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendsExtendedTest.gd"]) + assert_array(test_suites[0].get_children()).extract("name")\ + .contains_same_exactly_in_any_order([&"test_foo1"]) + assert_array(test_suites[1].get_children()).extract("name")\ + .contains_same_exactly_in_any_order([&"test_foo2", &"test_foo1"]) + assert_array(test_suites[2].get_children()).extract("name")\ + .contains_same_exactly_in_any_order([&"test_foo3", &"test_foo2", &"test_foo1"]) # finally free all scaned test suites for ts in test_suites: ts.free() @@ -305,7 +315,7 @@ func test_scan_by_inheritance_class_path() -> void: func test_get_test_case_line_number() -> void: - assert_int(GdUnitTestSuiteScanner.get_test_case_line_number("res://addons/gdUnit4/test/core/GdUnitTestSuiteScannerTest.gd", "get_test_case_line_number")).is_equal(307) + assert_int(GdUnitTestSuiteScanner.get_test_case_line_number("res://addons/gdUnit4/test/core/GdUnitTestSuiteScannerTest.gd", "get_test_case_line_number")).is_equal(317) assert_int(GdUnitTestSuiteScanner.get_test_case_line_number("res://addons/gdUnit4/test/core/GdUnitTestSuiteScannerTest.gd", "unknown")).is_equal(-1) @@ -333,22 +343,6 @@ func test_is_script_format_supported() -> void: assert_bool(GdUnitTestSuiteScanner._is_script_format_supported("res://exampe.tres")).is_false() -func test_load_parameterized_test_suite(): - var test_suite :GdUnitTestSuite = auto_free(GdUnitTestResourceLoader.load_test_suite("res://addons/gdUnit4/test/core/resources/testsuites/TestSuiteInvalidParameterizedTests.resource")) - - assert_that(test_suite.name).is_equal("TestSuiteInvalidParameterizedTests") - assert_that(test_suite.get_children()).extractv(extr("get_name"), extr("is_skipped"))\ - .contains_exactly_in_any_order([ - tuple("test_no_parameters", false), - tuple("test_parameterized_success", false), - tuple("test_parameterized_failed", false), - tuple("test_parameterized_to_less_args", true), - tuple("test_parameterized_to_many_args", true), - tuple("test_parameterized_to_less_args_at_index_1", true), - tuple("test_parameterized_invalid_struct", true), - tuple("test_parameterized_invalid_args", true)]) - - func test_resolve_test_suite_path() -> void: # forcing the use of a test folder next to the source folder assert_str(GdUnitTestSuiteScanner.resolve_test_suite_path("res://project/src/folder/myclass.gd", "test")).is_equal("res://project/test/folder/myclass_test.gd") @@ -369,3 +363,10 @@ func test_resolve_test_suite_path_with_src_folders() -> void: assert_str(GdUnitTestSuiteScanner.resolve_test_suite_path("res://project/folder/MyClass.gd", "")).is_equal("res://project/folder/MyClassTest.gd") assert_str(GdUnitTestSuiteScanner.resolve_test_suite_path("res://project/folder/myclass.gd", "/")).is_equal("res://project/folder/myclass_test.gd") assert_str(GdUnitTestSuiteScanner.resolve_test_suite_path("res://project/folder/MyClass.gd", "/")).is_equal("res://project/folder/MyClassTest.gd") + + +func test_scan_test_suite_without_tests() -> void: + var scanner :GdUnitTestSuiteScanner = auto_free(GdUnitTestSuiteScanner.new()) + var test_suites := scanner.scan("res://addons/gdUnit4/test/core/resources/testsuites/TestSuiteWithoutTests.gd") + + assert_that(test_suites).is_empty() diff --git a/addons/gdUnit4/test/core/parse/GdScriptParserTest.gd b/addons/gdUnit4/test/core/parse/GdScriptParserTest.gd index efc8913..cd2bd96 100644 --- a/addons/gdUnit4/test/core/parse/GdScriptParserTest.gd +++ b/addons/gdUnit4/test/core/parse/GdScriptParserTest.gd @@ -47,48 +47,48 @@ func test_parse_argument_timeout(): func test_parse_arguments(): assert_array(_parser.parse_arguments("func foo():")) \ .has_size(0) - + assert_array(_parser.parse_arguments("func foo() -> String:\n")) \ .has_size(0) - + assert_array(_parser.parse_arguments("func foo(arg1, arg2, name):")) \ .contains_exactly([ - GdFunctionArgument.new("arg1", TYPE_NIL), + GdFunctionArgument.new("arg1", TYPE_NIL), GdFunctionArgument.new("arg2", TYPE_NIL), GdFunctionArgument.new("name", TYPE_NIL)]) - + assert_array(_parser.parse_arguments('func foo(arg1 :int, arg2 :bool, name :String = "abc"):')) \ .contains_exactly([ - GdFunctionArgument.new("arg1", TYPE_INT), + GdFunctionArgument.new("arg1", TYPE_INT), GdFunctionArgument.new("arg2", TYPE_BOOL), GdFunctionArgument.new("name", TYPE_STRING, '"abc"')]) - + assert_array(_parser.parse_arguments('func bar(arg1 :int, arg2 :int = 23, name :String = "test") -> String:')) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_INT), GdFunctionArgument.new("arg2", TYPE_INT, "23"), GdFunctionArgument.new("name", TYPE_STRING, '"test"')]) - + assert_array(_parser.parse_arguments("func foo(arg1, arg2=value(1,2,3), name:=foo()):")) \ .contains_exactly([ - GdFunctionArgument.new("arg1", TYPE_NIL), + GdFunctionArgument.new("arg1", TYPE_NIL), GdFunctionArgument.new("arg2", GdObjects.TYPE_FUNC, "value(1,2,3)"), GdFunctionArgument.new("name", GdObjects.TYPE_FUNC, "foo()")]) # enum as prefix in value name assert_array(_parser.parse_arguments("func get_value( type := ENUM_A) -> int:"))\ .contains_exactly([GdFunctionArgument.new("type", TYPE_STRING, "ENUM_A")]) - + assert_array(_parser.parse_arguments("func create_timer(timeout :float) -> Timer:")) \ .contains_exactly([ GdFunctionArgument.new("timeout", TYPE_FLOAT)]) - + # array argument assert_array(_parser.parse_arguments("func foo(a :int, b :int, parameters = [[1, 2], [3, 4], [5, 6]]):")) \ .contains_exactly([ GdFunctionArgument.new("a", TYPE_INT), GdFunctionArgument.new("b", TYPE_INT), GdFunctionArgument.new("parameters", TYPE_ARRAY, "[[1, 2], [3, 4], [5, 6]]")]) - + assert_array(_parser.parse_arguments("func test_values(a:Vector2, b:Vector2, expected:Vector2, test_parameters:=[[Vector2.ONE,Vector2.ONE,Vector2(1,1)]]):"))\ .contains_exactly([ GdFunctionArgument.new("a", TYPE_VECTOR2), @@ -109,12 +109,12 @@ func test_parse_arguments_default_build_in_type_String(): .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_STRING, '"default"')]) - + assert_array(_parser.parse_arguments('func foo(arg1 :String, arg2 :="default"):')) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_STRING, '"default"')]) - + assert_array(_parser.parse_arguments('func foo(arg1 :String, arg2 :String ="default"):')) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), @@ -126,12 +126,12 @@ func test_parse_arguments_default_build_in_type_Boolean(): .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_BOOL, "false")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :=false):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_BOOL, "false")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :bool=false):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), @@ -143,12 +143,12 @@ func test_parse_arguments_default_build_in_type_Real(): .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_FLOAT, "3.14")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :=3.14):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_FLOAT, "3.14")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :float=3.14):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), @@ -160,27 +160,27 @@ func test_parse_arguments_default_build_in_type_Array(): .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_ARRAY, "[]")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :Array=Array()):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_ARRAY, "Array()")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :Array=[1, 2, 3]):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_ARRAY, "[1, 2, 3]")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :=[1, 2, 3]):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_ARRAY, "[1, 2, 3]")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2=[]):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_ARRAY, "[]")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :Array=[1, 2, 3], arg3 := false):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), @@ -193,12 +193,12 @@ func test_parse_arguments_default_build_in_type_Color(): .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_COLOR, "Color.RED")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :=Color.RED):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_COLOR, "Color.RED")]) - + assert_array(_parser.parse_arguments("func foo(arg1 :String, arg2 :Color=Color.RED):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), @@ -210,12 +210,12 @@ func test_parse_arguments_default_build_in_type_Vector(): .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_VECTOR3, "Vector3.FORWARD")]) - + assert_array(_parser.parse_arguments("func bar(arg1 :String, arg2 :=Vector3.FORWARD):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_VECTOR3, "Vector3.FORWARD")]) - + assert_array(_parser.parse_arguments("func bar(arg1 :String, arg2 :Vector3=Vector3.FORWARD):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), @@ -227,7 +227,7 @@ func test_parse_arguments_default_build_in_type_AABB(): .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), GdFunctionArgument.new("arg2", TYPE_AABB, "AABB()")]) - + assert_array(_parser.parse_arguments("func bar(arg1 :String, arg2 :AABB=AABB()):")) \ .contains_exactly([ GdFunctionArgument.new("arg1", TYPE_STRING), @@ -306,7 +306,7 @@ func test_parse_func_name(): func test_extract_source_code(): var path := GdObjects.extract_class_path(AdvancedTestClass) var rows = _parser.extract_source_code(path) - + var file_content := resource_as_array(path[0]) assert_array(rows).contains_exactly(file_content) @@ -335,20 +335,20 @@ func test_extract_source_code_inner_class_Area4D(): func test_extract_function_signature() -> void: var path := GdObjects.extract_class_path("res://addons/gdUnit4/test/mocker/resources/ClassWithCustomFormattings.gd") var rows = _parser.extract_source_code(path) - + assert_that(_parser.extract_func_signature(rows, 12))\ .is_equal(""" - func a1(set_name:String, path:String="", load_on_init:bool=false, + func a1(set_name:String, path:String="", load_on_init:bool=false, set_auto_save:bool=false, set_network_sync:bool=false ) -> void:""".dedent().trim_prefix("\n")) assert_that(_parser.extract_func_signature(rows, 19))\ .is_equal(""" - func a2(set_name:String, path:String="", load_on_init:bool=false, + func a2(set_name:String, path:String="", load_on_init:bool=false, set_auto_save:bool=false, set_network_sync:bool=false ) -> void:""".dedent().trim_prefix("\n")) assert_that(_parser.extract_func_signature(rows, 26))\ .is_equal(""" - func a3(set_name:String, path:String="", load_on_init:bool=false, + func a3(set_name:String, path:String="", load_on_init:bool=false, set_auto_save:bool=false, set_network_sync:bool=false ) :""".dedent().trim_prefix("\n")) assert_that(_parser.extract_func_signature(rows, 33))\ @@ -412,7 +412,7 @@ func test_parse_func_description(): assert_int(fd.return_type()).is_equal(GdObjects.TYPE_VARIANT) assert_array(fd.args()).is_empty() assert_str(fd.typeless()).is_equal("func foo() -> Variant:") - + # static function fd = _parser.parse_func_description("static func foo(arg1 :int, arg2:=false) -> String:", "clazz_name", [""], 22) assert_str(fd.name()).is_equal("foo") @@ -423,7 +423,7 @@ func test_parse_func_description(): GdFunctionArgument.new("arg2", TYPE_BOOL, "false") ]) assert_str(fd.typeless()).is_equal("static func foo(arg1, arg2=false) -> String:") - + # static function without return type fd = _parser.parse_func_description("static func foo(arg1 :int, arg2:=false):", "clazz_name", [""], 23) assert_str(fd.name()).is_equal("foo") @@ -439,19 +439,19 @@ func test_parse_func_description(): func test_parse_func_description_return_type_enum(): var result := _parser.parse("ClassWithEnumReturnTypes", ["res://addons/gdUnit4/test/mocker/resources/ClassWithEnumReturnTypes.gd"]) assert_result(result).is_success() - + var fd := _parser.parse_func_description("func get_enum() -> TEST_ENUM:", "ClassWithEnumReturnTypes", ["res://addons/gdUnit4/test/mocker/resources/ClassWithEnumReturnTypes.gd"], 33) assert_that(fd.name()).is_equal("get_enum") assert_that(fd.is_static()).is_false() assert_that(fd.return_type()).is_equal(GdObjects.TYPE_ENUM) - assert_that(fd._return_class).is_equal("TEST_ENUM") + assert_that(fd._return_class).is_equal("ClassWithEnumReturnTypes.TEST_ENUM") assert_that(fd.args()).is_empty() func test_parse_func_description_return_type_internal_class_enum(): var result := _parser.parse("ClassWithEnumReturnTypes", ["res://addons/gdUnit4/test/mocker/resources/ClassWithEnumReturnTypes.gd"]) assert_result(result).is_success() - + var fd := _parser.parse_func_description("func get_inner_class_enum() -> InnerClass.TEST_ENUM:", "ClassWithEnumReturnTypes", ["res://addons/gdUnit4/test/mocker/resources/ClassWithEnumReturnTypes.gd"], 33) assert_that(fd.name()).is_equal("get_inner_class_enum") assert_that(fd.is_static()).is_false() @@ -463,7 +463,7 @@ func test_parse_func_description_return_type_internal_class_enum(): func test_parse_func_description_return_type_external_class_enum(): var result := _parser.parse("ClassWithEnumReturnTypes", ["res://addons/gdUnit4/test/mocker/resources/ClassWithEnumReturnTypes.gd"]) assert_result(result).is_success() - + var fd := _parser.parse_func_description("func get_external_class_enum() -> CustomEnums.TEST_ENUM:", "ClassWithEnumReturnTypes", ["res://addons/gdUnit4/test/mocker/resources/ClassWithEnumReturnTypes.gd"], 33) assert_that(fd.name()).is_equal("get_external_class_enum") assert_that(fd.is_static()).is_false() @@ -477,7 +477,7 @@ func test_parse_class_inherits(): var clazz_name := GdObjects.extract_class_name_from_class_path(clazz_path) var result := _parser.parse(clazz_name, clazz_path) assert_result(result).is_success() - + # verify class extraction var clazz_desccriptor :GdClassDescriptor = result.value() assert_object(clazz_desccriptor).is_not_null() @@ -487,7 +487,7 @@ func test_parse_class_inherits(): GdFunctionDescriptor.new("foo2", 5, false, false, false, GdObjects.TYPE_VARIANT, "", []), GdFunctionDescriptor.new("bar2", 8, false, false, false, TYPE_STRING, "", []) ]) - + # extends from CustomResourceTestClass clazz_desccriptor = clazz_desccriptor.parent() assert_object(clazz_desccriptor).is_not_null() @@ -504,7 +504,7 @@ func test_parse_class_inherits(): ]), GdFunctionDescriptor.new("foo5", 16, false, false, false, GdObjects.TYPE_VARIANT, "", []), ]) - + # no other class extends clazz_desccriptor = clazz_desccriptor.parent() assert_object(clazz_desccriptor).is_null() @@ -540,17 +540,17 @@ func test_is_func_end() -> void: func test_extract_func_signature_multiline() -> void: var source_code = """ - + func test_parameterized(a: int, b :int, c :int, expected :int, parameters = [ [1, 2, 3, 6], [3, 4, 5, 11], [6, 7, 8, 21] ]): - + assert_that(a+b+c).is_equal(expected) """.dedent().split("\n") - + var fs = _parser.extract_func_signature(source_code, 0) - + assert_that(fs).is_equal(""" func test_parameterized(a: int, b :int, c :int, expected :int, parameters = [ [1, 2, 3, 6], @@ -563,7 +563,7 @@ func test_extract_func_signature_multiline() -> void: func test_parse_func_description_paramized_test(): var fd = _parser.parse_func_description("functest_parameterized(a:int,b:int,c:int,expected:int,parameters=[[1,2,3,6],[3,4,5,11],[6,7,8,21]]):", "class", ["path"], 22) - + assert_that(fd).is_equal(GdFunctionDescriptor.new("test_parameterized", 22, false, false, false, GdObjects.TYPE_VARIANT, "", [ GdFunctionArgument.new("a", TYPE_INT), GdFunctionArgument.new("b", TYPE_INT), @@ -573,6 +573,34 @@ func test_parse_func_description_paramized_test(): ])) +func test_parse_func_description_paramized_test_with_comments() -> void: + var source_code = """ + func test_parameterized(a: int, b :int, c :int, expected :int, parameters = [ + # before data set + [1, 2, 3, 6], # after data set + # between data sets + [3, 4, 5, 11], + [6, 7, 'string #ABCD', 21], # dataset with [comment] singn + [6, 7, "string #ABCD", 21] # dataset with "#comment" singn + #eof + ]): + pass + """.dedent().split("\n") + + var fs = _parser.extract_func_signature(source_code, 0) + + assert_that(fs).is_equal(""" + func test_parameterized(a: int, b :int, c :int, expected :int, parameters = [ + [1, 2, 3, 6], + [3, 4, 5, 11], + [6, 7, 'string #ABCD', 21], + [6, 7, "string #ABCD", 21] + ]):""" + .dedent() + .trim_prefix("\n") + ) + + func test_parse_func_descriptor_with_fuzzers(): var source_code := """ func test_foo(fuzzer_a = fuzz_a(), fuzzer_b := fuzz_b(), @@ -583,7 +611,7 @@ func test_parse_func_descriptor_with_fuzzers(): """.split("\n") var fs = _parser.extract_func_signature(source_code, 0) var fd = _parser.parse_func_description(fs, "class", ["path"], 22) - + assert_that(fd).is_equal(GdFunctionDescriptor.new("test_foo", 22, false, false, false, GdObjects.TYPE_VARIANT, "", [ GdFunctionArgument.new("fuzzer_a", GdObjects.TYPE_FUZZER, "fuzz_a()"), GdFunctionArgument.new("fuzzer_b", GdObjects.TYPE_FUZZER, "fuzz_b()"), diff --git a/addons/gdUnit4/test/core/parse/GdUnitTestParameterSetResolverTest.gd b/addons/gdUnit4/test/core/parse/GdUnitTestParameterSetResolverTest.gd index 81bfc1f..5372f2b 100644 --- a/addons/gdUnit4/test/core/parse/GdUnitTestParameterSetResolverTest.gd +++ b/addons/gdUnit4/test/core/parse/GdUnitTestParameterSetResolverTest.gd @@ -37,7 +37,7 @@ func test_example_c(a: Object, b: Object, test_parameters=[ @warning_ignore("unused_parameter") -func test_resolve_paramaters_static(a: int, b: int, test_parameters=[ +func test_resolve_parameters_static(a: int, b: int, test_parameters=[ [1, 10], [2, 20] ]) -> void: @@ -45,7 +45,7 @@ func test_resolve_paramaters_static(a: int, b: int, test_parameters=[ @warning_ignore("unused_parameter") -func test_resolve_paramaters_at_runtime(a: int, b: int, test_parameters=[ +func test_resolve_parameters_at_runtime(a: int, b: int, test_parameters=[ [1, _test_param1], [2, _test_param2], [3, 30] @@ -53,6 +53,19 @@ func test_resolve_paramaters_at_runtime(a: int, b: int, test_parameters=[ pass +@warning_ignore("unused_parameter") +func test_parameterized_with_comments(a: int, b :int, c :String, expected :int, test_parameters = [ + # before data set + [1, 2, '3', 6], # after data set + # between data sets + [3, 4, '5', 11], + [6, 7, 'string #ABCD', 21], # dataset with [comment] singn + [6, 7, "string #ABCD", 21] # dataset with "comment" singn + #eof +]): + pass + + func build_param(value: float) -> Vector3: return Vector3(value, value, value) @@ -67,10 +80,10 @@ func test_example_d(a: Vector3, b: Vector3, test_parameters=[ class TestObj extends RefCounted: var _value: String - + func _init(value: String): _value = value - + func _to_string() -> String: return _value @@ -86,26 +99,27 @@ func test_load_parameter_sets() -> void: var tc := get_test_case("test_example_a") assert_array(tc.parameter_set_resolver().load_parameter_sets(tc)) \ .is_equal([[1, 2], [3, 4]]) - + tc = get_test_case("test_example_b") assert_array(tc.parameter_set_resolver().load_parameter_sets(tc)) \ .is_equal([[Vector2.ZERO, Vector2.ONE], [Vector2(1.1, 3.2), Vector2.DOWN]]) - + tc = get_test_case("test_example_c") assert_array(tc.parameter_set_resolver().load_parameter_sets(tc)) \ .is_equal([[Resource.new(), Resource.new()], [Resource.new(), null]]) - + tc = get_test_case("test_example_d") assert_array(tc.parameter_set_resolver().load_parameter_sets(tc)) \ .is_equal([[Vector3(1, 1, 1), Vector3(3, 3, 3)], [Vector3.BACK, Vector3.UP]]) - + tc = get_test_case("test_example_e") assert_array(tc.parameter_set_resolver().load_parameter_sets(tc)) \ .is_equal([[TestObj.new("abc"), TestObj.new("def"), "abcdef"]]) func test_load_parameter_sets_at_runtime() -> void: - var tc := get_test_case("test_resolve_paramaters_at_runtime") + var tc := get_test_case("test_resolve_parameters_at_runtime") + assert_that(tc).is_not_null() # check the parameters resolved at runtime assert_array(tc.parameter_set_resolver().load_parameter_sets(tc)) \ .is_equal([ @@ -117,33 +131,92 @@ func test_load_parameter_sets_at_runtime() -> void: [3, 30]]) +func test_load_parameter_with_comments() -> void: + var tc := get_test_case("test_parameterized_with_comments") + assert_that(tc).is_not_null() + # check the parameters resolved at runtime + assert_array(tc.parameter_set_resolver().load_parameter_sets(tc)) \ + .is_equal([ + [1, 2, '3', 6], + [3, 4, '5', 11], + [6, 7, 'string #ABCD', 21], + [6, 7, "string #ABCD", 21]]) + + func test_build_test_case_names_on_static_parameter_set() -> void: - var test_case := get_test_case("test_resolve_paramaters_static") + var test_case := get_test_case("test_resolve_parameters_static") var resolver := test_case.parameter_set_resolver() - + assert_array(resolver.build_test_case_names(test_case))\ .contains_exactly([ - "test_resolve_paramaters_static:0 [1, 10]", - "test_resolve_paramaters_static:1 [2, 20]"]) + "test_resolve_parameters_static:0 [1, 10]", + "test_resolve_parameters_static:1 [2, 20]"]) assert_that(resolver.is_parameter_sets_static()).is_true() assert_that(resolver.is_parameter_set_static(0)).is_true() assert_that(resolver.is_parameter_set_static(1)).is_true() func test_build_test_case_names_on_runtime_parameter_set() -> void: - var test_case := get_test_case("test_resolve_paramaters_at_runtime") + var test_case := get_test_case("test_resolve_parameters_at_runtime") var resolver := test_case.parameter_set_resolver() - + assert_array(resolver.build_test_case_names(test_case))\ .contains_exactly([ - "test_resolve_paramaters_at_runtime:0 [1, _test_param1]", - "test_resolve_paramaters_at_runtime:1 [2, _test_param2]", - "test_resolve_paramaters_at_runtime:2 [3, 30]"]) + "test_resolve_parameters_at_runtime:0 [1, _test_param1]", + "test_resolve_parameters_at_runtime:1 [2, _test_param2]", + "test_resolve_parameters_at_runtime:2 [3, 30]"]) assert_that(resolver.is_parameter_sets_static()).is_false() assert_that(resolver.is_parameter_set_static(0)).is_false() assert_that(resolver.is_parameter_set_static(1)).is_false() assert_that(resolver.is_parameter_set_static(2)).is_false() +func test_validate_test_parameter_set(): + var test_suite :GdUnitTestSuite = auto_free(GdUnitTestResourceLoader.load_test_suite("res://addons/gdUnit4/test/core/resources/testsuites/TestSuiteInvalidParameterizedTests.resource")) + + assert_is_not_skipped(test_suite, "test_no_parameters") + assert_is_not_skipped(test_suite, "test_parameterized_success") + assert_is_not_skipped(test_suite, "test_parameterized_failed") + assert_is_skipped(test_suite, "test_parameterized_to_less_args")\ + .contains("The parameter set at index [0] does not match the expected input parameters!")\ + .contains("The test case requires [3] input parameters, but the set contains [4]") + assert_is_skipped(test_suite, "test_parameterized_to_many_args")\ + .contains("The parameter set at index [0] does not match the expected input parameters!")\ + .contains("The test case requires [5] input parameters, but the set contains [4]") + assert_is_skipped(test_suite, "test_parameterized_to_less_args_at_index_1")\ + .contains("The parameter set at index [1] does not match the expected input parameters!")\ + .contains("The test case requires [3] input parameters, but the set contains [4]") + assert_is_skipped(test_suite, "test_parameterized_invalid_struct")\ + .contains("The parameter set at index [1] does not match the expected input parameters!")\ + .contains("The test case requires [3] input parameters, but the set contains [1]") + assert_is_skipped(test_suite, "test_parameterized_invalid_args")\ + .contains("The parameter set at index [1] does not match the expected input parameters!")\ + .contains("The value '4' does not match the required input parameter .") + + +func assert_is_not_skipped(test_suite :GdUnitTestSuite, test_case :String) -> void: + # set actual execution context for this test suite + test_suite.__execution_context = GdUnitExecutionContext.new(test_suite.get_name()) + var test :_TestCase = test_suite.find_child(test_case, true, false) + if test.is_parameterized(): + # to load parameter set and force validate + var resolver := test.parameter_set_resolver() + resolver.build_test_case_names(test) + resolver.load_parameter_sets(test, true) + assert_that(test.is_skipped()).is_false() + + +func assert_is_skipped(test_suite :GdUnitTestSuite, test_case :String) -> GdUnitStringAssert: + # set actual execution context for this test suite + test_suite.__execution_context = GdUnitExecutionContext.new(test_suite.get_name()) + var test :_TestCase = test_suite.find_child(test_case, true, false) + if test.is_parameterized(): + # to load parameter set and force validate + var resolver := test.parameter_set_resolver() + resolver.build_test_case_names(test) + resolver.load_parameter_sets(test, true) + assert_that(test.is_skipped()).is_true() + return assert_str(test.skip_info()) + func get_test_case(name: String) -> _TestCase: return find_child(name, true, false) diff --git a/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/BaseTest.gd b/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/BaseTest.gd index f2b5d8f..d974340 100644 --- a/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/BaseTest.gd +++ b/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/BaseTest.gd @@ -1,5 +1,22 @@ class_name BaseTest extends GdUnitTestSuite + +func before(): + pass + + +func after(): + pass + + +func before_test(): + pass + + +func after_test(): + pass + + func test_foo1() -> void: pass diff --git a/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendedTest.gd b/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendedTest.gd index 0b94606..068bba0 100644 --- a/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendedTest.gd +++ b/addons/gdUnit4/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendedTest.gd @@ -1,5 +1,14 @@ class_name ExtendedTest extends BaseTest + +func before_test(): + pass + + +func after_test(): + pass + + func test_foo2() -> void: pass diff --git a/addons/gdUnit4/test/core/resources/testsuites/TestSuiteWithoutTests.gd b/addons/gdUnit4/test/core/resources/testsuites/TestSuiteWithoutTests.gd new file mode 100644 index 0000000..9d1564e --- /dev/null +++ b/addons/gdUnit4/test/core/resources/testsuites/TestSuiteWithoutTests.gd @@ -0,0 +1,9 @@ +extends GdUnitTestSuite + + +func before(): + pass + + +func foo(): + pass diff --git a/addons/gdUnit4/test/mocker/resources/ClassWithCustomFormattings.gd b/addons/gdUnit4/test/mocker/resources/ClassWithCustomFormattings.gd index 11f8e02..c0b53c5 100644 --- a/addons/gdUnit4/test/mocker/resources/ClassWithCustomFormattings.gd +++ b/addons/gdUnit4/test/mocker/resources/ClassWithCustomFormattings.gd @@ -3,28 +3,28 @@ extends Object var _message @warning_ignore("unused_parameter") -func _init(message:String, path:String="", load_on_init:bool=false, +func _init(message:String, path:String="", load_on_init:bool=false, set_auto_save:bool=false, set_network_sync:bool=false ) -> void: _message = message @warning_ignore("unused_parameter") -func a1(set_name:String, path:String="", load_on_init:bool=false, +func a1(set_name:String, path:String="", load_on_init:bool=false, set_auto_save:bool=false, set_network_sync:bool=false ) -> void: pass @warning_ignore("unused_parameter") -func a2(set_name:String, path:String="", load_on_init:bool=false, +func a2(set_name:String, path:String="", load_on_init:bool=false, set_auto_save:bool=false, set_network_sync:bool=false ) -> void: pass @warning_ignore("unused_parameter") -func a3(set_name:String, path:String="", load_on_init:bool=false, +func a3(set_name:String, path:String="", load_on_init:bool=false, set_auto_save:bool=false, set_network_sync:bool=false ) : pass diff --git a/addons/gdUnit4/test/mono/GdUnit4CSharpApiTest.cs b/addons/gdUnit4/test/mono/GdUnit4CSharpApiTest.cs index af5115b..584503e 100644 --- a/addons/gdUnit4/test/mono/GdUnit4CSharpApiTest.cs +++ b/addons/gdUnit4/test/mono/GdUnit4CSharpApiTest.cs @@ -16,7 +16,7 @@ public void IsTestSuite() [TestCase] public void GetVersion() { - AssertThat(GdUnit4CSharpApi.Version()).IsEqual("4.2.1.0"); + AssertThat(GdUnit4CSharpApi.Version()).IsEqual("4.2.2.0"); } } } diff --git a/addons/log/plugin.gd b/addons/log/plugin.gd index e002992..15356b8 100644 --- a/addons/log/plugin.gd +++ b/addons/log/plugin.gd @@ -1,19 +1,3 @@ @tool extends EditorPlugin - -# func _enter_tree(): -# Log.pr(" config:", Log.config) - -# # parsing command line args -# # var arguments = {} -# # for argument in OS.get_cmdline_args(): -# # if argument.find("=") > -1: -# # var key_value = argument.split("=") -# # arguments[key_value[0].lstrip("--")] = key_value[1] -# # else: -# # arguments[argument.lstrip("--")] = true -# # Log.pr("args", arguments) - -# func _exit_tree(): -# Log.pr("") diff --git a/project.godot b/project.godot index 33a767e..000e74a 100644 --- a/project.godot +++ b/project.godot @@ -17,7 +17,7 @@ config/icon="res://icon.svg" [editor_plugins] -enabled=PackedStringArray("res://addons/gd-plug-ui/plugin.cfg", "res://addons/log/plugin.cfg") +enabled=PackedStringArray("res://addons/gdUnit4/plugin.cfg", "res://addons/log/plugin.cfg") [gdunit4]