Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GD-120: Fix signal 11 crash when execute tests via GdUnitCmdTool #121

Merged
merged 9 commits into from
Feb 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/unit-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ runs:
shell: bash
run: |
chmod +x ./runtest.sh
./runtest.sh --add ${{ inputs.test-includes }} --continue
./runtest.sh --add ${{ inputs.test-includes }} --continue --verbose


# not tested yet
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
workflow_dispatch:

concurrency:
group: ci-pr-${{ github.event.number }}
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
Expand Down
126 changes: 84 additions & 42 deletions addons/gdUnit4/bin/GdUnitCmdTool.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ extends SceneTree

#warning-ignore-all:return_value_discarded
class CLIRunner extends Node:

enum {
INIT,
RUN,
STOP,
EXIT
}

const DEFAULT_REPORT_COUNT = 20
const RETURN_SUCCESS = 0
const RETURN_ERROR = 100
Expand Down Expand Up @@ -41,7 +42,8 @@ class CLIRunner extends Node:
CmdOption.new("--info", "", "Shows the GdUnit version info"),
CmdOption.new("--selftest", "", "Runs the GdUnit self test"),
])



func _ready():
_state = INIT
_report_dir = GdUnitTools.current_dir() + "reports"
Expand All @@ -56,11 +58,12 @@ class CLIRunner extends Node:
if err != OK:
prints("gdUnitSignals failed")
push_error("Error checked startup, can't connect executor for 'send_event'")
get_tree().quit(RETURN_ERROR)
quit(RETURN_ERROR)
add_child(_executor)
_rtf = RichTextLabel.new()
add_child(_rtf)



func _process(_delta):
match _state:
INIT:
Expand All @@ -82,13 +85,24 @@ class CLIRunner extends Node:
STOP:
_state = EXIT
_on_gdunit_event(GdUnitStop.new())
GdUnitSingleton.dispose()
get_tree().quit(report_exit_code(_report))

quit(report_exit_code(_report))


func quit(code :int) -> void:
if is_instance_valid(_executor):
_executor.free()
if is_instance_valid(_rtf):
_rtf.free()
GdUnitSignals.dispose()
GdUnitSingleton.dispose()
get_tree().quit(code)


func set_report_dir(path :String) -> void:
_report_dir = ProjectSettings.globalize_path(GdUnitTools.make_qualified_path(path))
_console.prints_color("Set write reports to %s" % _report_dir, Color.DEEP_SKY_BLUE)



func set_report_count(count :String) -> void:
var report_count := count.to_int()
if report_count < 1:
Expand All @@ -97,23 +111,27 @@ class CLIRunner extends Node:
else:
_console.prints_color("Set report history count to %s" % count, Color.DEEP_SKY_BLUE)
_report_max = report_count



func disable_fail_fast() -> void:
_console.prints_color("Disabled fail fast!", Color.DEEP_SKY_BLUE)
_executor.fail_fast(false)



func run_self_test() -> void:
_console.prints_color("Run GdUnit4 self tests.", Color.DEEP_SKY_BLUE)
disable_fail_fast()
_runner_config.self_test()



func show_version() -> void:
_console.prints_color("Godot %s" % Engine.get_version_info().get("string"), Color.DARK_SALMON)
var config = ConfigFile.new()
config.load('addons/gdUnit4/plugin.cfg')
_console.prints_color("GdUnit4 %s" % config.get_value('plugin', 'version'), Color.DARK_SALMON)
get_tree().quit(RETURN_SUCCESS)

quit(RETURN_SUCCESS)


func show_options(show_advanced :bool = false) -> void:
_console.prints_color(" Usage:", Color.DARK_SALMON)
_console.prints_color(" runtest -a <directory|path of testsuite>", Color.DARK_SALMON)
Expand All @@ -125,26 +143,31 @@ class CLIRunner extends Node:
_console.prints_color("-- Advanced options --------------------------------------------------------------------------", Color.DARK_SALMON).new_line()
for option in _cmd_options.advanced_options():
descripe_option(option)



func descripe_option(cmd_option :CmdOption) -> void:
_console.print_color(" %-40s" % str(cmd_option.commands()), Color.CORNFLOWER_BLUE)
_console.prints_color(cmd_option.description(), Color.LIGHT_GREEN)
if not cmd_option.help().is_empty():
_console.prints_color("%-4s %s" % ["", cmd_option.help()], Color.DARK_TURQUOISE)
_console.new_line()



func load_test_config(path :String = "GdUnitRunner.cfg") -> void:
_console.print_color("Loading test configuration %s\n" % path, Color.CORNFLOWER_BLUE)
_runner_config.load(path)



func show_help() -> void:
show_options()
get_tree().quit(RETURN_SUCCESS)

quit(RETURN_SUCCESS)


func show_advanced_help() -> void:
show_options(true)
get_tree().quit(RETURN_SUCCESS)

quit(RETURN_SUCCESS)


func gdUnitInit() -> void:
_console.prints_color("----------------------------------------------------------------------------------------------", Color.DARK_SALMON)
_console.prints_color(" GdUnit4 Comandline Tool", Color.DARK_SALMON)
Expand All @@ -157,7 +180,7 @@ class CLIRunner extends Node:
_console.prints_error(result.error_message())
_console.prints_error("Abnormal exit with %d" % RETURN_ERROR)
_state = STOP
get_tree().quit(RETURN_ERROR)
quit(RETURN_ERROR)
return

if result.is_empty():
Expand All @@ -182,33 +205,33 @@ class CLIRunner extends Node:
if result.is_error():
_console.prints_error(result.error_message())
_state = STOP
get_tree().quit(RETURN_ERROR)
quit(RETURN_ERROR)

_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!")
_console.prints_color("Exit code: %d" % RETURN_SUCCESS, Color.DARK_SALMON)
_state = STOP
get_tree().quit(RETURN_SUCCESS)
quit(RETURN_SUCCESS)

# wait comamnd handler is finish, e.g. exit program checked show help
await get_tree().process_frame
var total_test_count = _collect_test_case_count(_test_suites_to_process)
_on_gdunit_event(GdUnitInit.new(_test_suites_to_process.size(), total_test_count))

func load_testsuites(config :GdUnitRunnerConfig) -> Array:
var test_suites_to_process = Array()


func load_testsuites(config :GdUnitRunnerConfig) -> Array[Node]:
var test_suites_to_process :Array[Node] = []
var to_execute := config.to_execute()
# scan for the requested test suites
var _scanner := GdUnitTestSuiteScanner.new()
for resource_path in to_execute.keys():
var selected_tests :Array = to_execute.get(resource_path)
var scaned_suites = _scanner.scan(resource_path)
var selected_tests :PackedStringArray = to_execute.get(resource_path)
var scaned_suites := _scanner.scan(resource_path)
skip_test_case(scaned_suites, selected_tests)
test_suites_to_process += scaned_suites
test_suites_to_process.append_array(scaned_suites)
skip_suites(test_suites_to_process, config)
return test_suites_to_process



func skip_test_case(test_suites :Array, test_case_names :Array) -> void:
if test_case_names.is_empty():
return
Expand All @@ -217,12 +240,14 @@ class CLIRunner extends Node:
if not test_case_names.has(test_case.get_name()):
test_suite.remove_child(test_case)
test_case.free()



func skip_suites(test_suites :Array, config :GdUnitRunnerConfig) -> void:
var skipped := config.skipped()
for test_suite in test_suites:
skip_suite(test_suite, skipped)



func skip_suite(test_suite :Node, skipped :Dictionary) -> void:
var skipped_suites := skipped.keys()
if skipped_suites.is_empty():
Expand All @@ -249,16 +274,19 @@ class CLIRunner extends Node:
_console.prints_warning("Skip test case %s:%s" % [suite_to_skip, test_to_skip])
else:
_console.prints_error("Can't skip test '%s' checked test suite '%s', no test with given name exists!" % [test_to_skip, suite_to_skip])



func _collect_test_case_count(testSuites :Array) -> int:
var total :int = 0
for test_suite in testSuites:
total += (test_suite as Node).get_child_count()
return total



func PublishEvent(data) -> void:
_on_gdunit_event(GdUnitEvent.new().deserialize(data.AsDictionary()))



func _on_gdunit_event(event :GdUnitEvent):
match event.type():
GdUnitEvent.INIT:
Expand Down Expand Up @@ -291,7 +319,8 @@ class CLIRunner extends Node:
event.elapsed_time())
_report.update_testcase_report(event.resource_path(), test_report)
print_status(event)



func report_exit_code(report :GdUnitHtmlReport) -> int:
if report.error_count() + report.failure_count() > 0:
_console.prints_color("Exit code: %d" % RETURN_ERROR, Color.FIREBRICK)
Expand All @@ -301,7 +330,8 @@ class CLIRunner extends Node:
return RETURN_WARNING
_console.prints_color("Exit code: %d" % RETURN_SUCCESS, Color.DARK_SALMON)
return RETURN_SUCCESS



func print_status(event :GdUnitEvent) -> void:
match event.type():
GdUnitEvent.TESTSUITE_BEFORE:
Expand All @@ -316,7 +346,8 @@ class CLIRunner extends Node:
GdUnitEvent.TESTSUITE_AFTER:
_print_status(event)
_console.prints_color(" | %d total | %d error | %d failed | %d skipped | %d orphans |\n" % [_report.test_count(), _report.error_count(), _report.failure_count(), _report.skipped_count(), _report.orphan_count()], Color.ANTIQUE_WHITE)



func _print_failure_report(reports :Array) -> void:
for report in reports:
_rtf.clear()
Expand All @@ -326,7 +357,8 @@ class CLIRunner extends Node:
for line in _rtf.text.split("\n"):
_console.prints_color(" %s" % line, Color.DARK_TURQUOISE)
_console.new_line()



func _print_status(event :GdUnitEvent) -> void:
if event.is_skipped():
_console.print_color("SKIPPED", Color.GOLDENROD, CmdConsole.BOLD|CmdConsole.ITALIC)
Expand All @@ -338,6 +370,16 @@ class CLIRunner extends Node:
_console.print_color("PASSED", Color.FOREST_GREEN, CmdConsole.BOLD)
_console.prints_color(" %s" % LocalTime.elapsed(event.elapsed_time()), Color.CORNFLOWER_BLUE)


var _cli_runner :CLIRunner


func _initialize():
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED)
root.add_child(CLIRunner.new())
_cli_runner = CLIRunner.new()
root.add_child(_cli_runner)


func _finalize():
prints("Finallize ..")
_cli_runner.free()
19 changes: 10 additions & 9 deletions addons/gdUnit4/src/GdUnitTestSuite.gd
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,6 @@ func skip(skipped :bool) -> void:
func is_failure(_expected_failure :String = NO_ARG) -> bool:
return get_meta(GdUnitAssertImpl.GD_TEST_FAILURE) if has_meta(GdUnitAssertImpl.GD_TEST_FAILURE) else false

# 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 is_skipped() -> bool:
return get_meta("gd_skipped") if has_meta("gd_skipped") else false

Expand Down Expand Up @@ -378,6 +369,16 @@ func assert_signal(instance :Object, expect_result :int = GdUnitAssert.EXPECT_SU
func assert_fail(assertion :GdUnitAssert) -> GdUnitAssert:
return assertion

# 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 assert_not_yet_implemented():
GdUnitAssertImpl.new(self, null).test_fail()

Expand Down
2 changes: 1 addition & 1 deletion addons/gdUnit4/src/core/GdUnitClassDoubler.gd
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static func double_functions(instance :Object, clazz_name :String, clazz_path :P
#prints("no virtual func implemented",clazz_name, func_descriptor.name() )
continue
functions.append(func_descriptor.name())
doubled_source += func_doubler.double(func_descriptor)
doubled_source.append_array(func_doubler.double(func_descriptor))
return doubled_source


Expand Down
1 change: 0 additions & 1 deletion addons/gdUnit4/src/core/GdUnitExecutor.gd
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
class_name GdUnitExecutor
extends Node

signal ExecutionCompleted()
Expand Down
16 changes: 11 additions & 5 deletions addons/gdUnit4/src/core/GdUnitMemoryPool.gd
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ var _monitors := {


class MemoryStore extends RefCounted:
var _store :Array[Variant] = Array()
var _store :Array[Variant] = []


func _notification(what):
if what == NOTIFICATION_PREDELETE:
while not _store.is_empty():
var value := _store.pop_front()
GdUnitTools.free_instance(value)


static func pool(pool :POOL) -> MemoryStore:
var pool_name :String = POOL.keys()[pool]
Expand All @@ -41,10 +49,8 @@ class MemoryStore extends RefCounted:


static func release(pool :POOL) -> void:
var mp := pool(pool)
while not mp._store.is_empty():
var value := mp._store.pop_front()
GdUnitTools.free_instance(value)
var pool_name :String = POOL.keys()[pool]
GdUnitSingleton.unregister(pool_name)


var _current :POOL
Expand Down
2 changes: 2 additions & 0 deletions addons/gdUnit4/src/core/GdUnitRunner.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ extends Node

signal sync_rpc_id_result_received

const GdUnitExecutor = preload("res://addons/gdUnit4/src/core/GdUnitExecutor.gd")

@onready var _client :GdUnitTcpClient = $GdUnitTcpClient
@onready var _executor :GdUnitExecutor = $GdUnitExecutor

Expand Down
Loading