Skip to content

Commit

Permalink
update gdunit to 4.2 (#157)
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbrain authored Dec 2, 2023
1 parent d758bae commit a0376f7
Show file tree
Hide file tree
Showing 110 changed files with 2,152 additions and 1,420 deletions.
30 changes: 13 additions & 17 deletions addons/gdUnit4/bin/GdUnitCmdTool.gd
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ class CLIRunner extends Node:
var _state = READY
var _test_suites_to_process :Array
var _executor
var _cs_executor
var _report :GdUnitHtmlReport
var _report_dir: String
var _report_max: int = DEFAULT_REPORT_COUNT
var _runner_config := GdUnitRunnerConfig.new()
var _console := CmdConsole.new()
var _cs_executor
var _cmd_options: = CmdOptions.new([
CmdOption.new("-a, --add", "-a <directory|path of testsuite>", "Adds the given test suite or directory to the execution pipeline.", TYPE_STRING),
CmdOption.new("-i, --ignore", "-i <testsuite_name|testsuite_name:test-name>", "Adds the given test suite or test case to the ignore list.", TYPE_STRING),
Expand All @@ -50,19 +50,18 @@ class CLIRunner extends Node:
func _ready():
_state = INIT
_report_dir = GdUnitTools.current_dir() + "reports"
_executor = load("res://addons/gdUnit4/src/core/GdUnitExecutor.gd").new()
_executor = load("res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd").new()
# stop checked first test failure to fail fast
_executor.fail_fast(true)

if GdUnitTools.is_mono_supported():
_cs_executor = GdUnit3MonoAPI.create_executor(self)

if GdUnit4MonoApiLoader.is_mono_supported():
prints("GdUnit4Mono Version %s loaded." % GdUnit4MonoApiLoader.version())
_cs_executor = GdUnit4MonoApiLoader.create_executor(self)
var err = GdUnitSignals.instance().gdunit_event.connect(Callable(self, "_on_gdunit_event"))
if err != OK:
prints("gdUnitSignals failed")
push_error("Error checked startup, can't connect executor for 'send_event'")
quit(RETURN_ERROR)
add_child(_executor)


func _process(_delta):
Expand All @@ -78,10 +77,11 @@ class CLIRunner extends Node:
set_process(false)
# process next test suite
var test_suite := _test_suites_to_process.pop_front() as Node
add_child(test_suite)
var executor = _cs_executor if GdObjects.is_cs_test_suite(test_suite) else _executor
executor.Execute(test_suite)
await executor.ExecutionCompleted
if _cs_executor != null and _cs_executor.IsExecutable(test_suite):
_cs_executor.Execute(test_suite)
await _cs_executor.ExecutionCompleted
else:
await _executor.execute(test_suite)
set_process(true)
STOP:
_state = EXIT
Expand All @@ -90,9 +90,8 @@ class CLIRunner extends Node:


func quit(code :int) -> void:
if is_instance_valid(_executor):
_executor.free()
GdUnitTools.dispose_all()
await GdUnitMemoryObserver.gc_on_guarded_instances()
await get_tree().physics_frame
get_tree().quit(code)

Expand Down Expand Up @@ -290,8 +289,8 @@ class CLIRunner extends Node:
return total


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


func _on_gdunit_event(event :GdUnitEvent):
Expand Down Expand Up @@ -399,9 +398,6 @@ func _initialize():

func _finalize():
prints("Finallize ..")
_cli_runner.free()
prints("-Orphan nodes report-----------------------")
Window.print_orphan_nodes()
prints("-SceneTree report-----------------------")
root.print_tree_pretty()
prints("Finallize .. done")
8 changes: 4 additions & 4 deletions addons/gdUnit4/bin/GdUnitCopyLog.gd
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ func _patch_report(report_path :String, godot_log :String) -> void:
index_file.seek(0)
index_file.store_string(content)

func _copy_and_pach(from_file: String, to_dir: String) -> Result:
func _copy_and_pach(from_file: String, to_dir: String) -> GdUnitResult:
var result := GdUnitTools.copy_file(from_file, to_dir)
if result.is_error():
return result
var file := FileAccess.open(from_file, FileAccess.READ)
if file == null:
return Result.error("Can't find file '%s'. Error: %s" % [from_file, GdUnitTools.error_as_string(FileAccess.get_open_error())])
return GdUnitResult.error("Can't find file '%s'. Error: %s" % [from_file, GdUnitTools.error_as_string(FileAccess.get_open_error())])
var content := file.get_as_text()
# patch out console format codes
for color_index in range(0, 256):
Expand All @@ -110,9 +110,9 @@ func _copy_and_pach(from_file: String, to_dir: String) -> Result:
var to_file := to_dir + "/" + from_file.get_file()
file = FileAccess.open(to_file, FileAccess.WRITE)
if file == null:
return Result.error("Can't open to write '%s'. Error: %s" % [to_file, GdUnitTools.error_as_string(FileAccess.get_open_error())])
return GdUnitResult.error("Can't open to write '%s'. Error: %s" % [to_file, GdUnitTools.error_as_string(FileAccess.get_open_error())])
file.store_string(content)
return Result.empty()
return GdUnitResult.empty()

func reports_available() -> bool:
return DirAccess.dir_exists_absolute(_report_root_path)
51 changes: 30 additions & 21 deletions addons/gdUnit4/bin/ProjectScanner.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ extends SceneTree

const CmdConsole = preload("res://addons/gdUnit4/src/cmd/CmdConsole.gd")

var scanner := ProjectScanner.new()
var scanner := SourceScanner.new()

func _initialize():
set_auto_accept_quit(false)
root.add_child(scanner)


func _finalize():
if Engine.get_version_info().hex < 0x40100 or Engine.get_version_info().hex > 0x40101:
print("Finalize scanner ..")
scanner.free()
if Engine.get_version_info().hex < 0x40100 or Engine.get_version_info().hex > 0x40101:
prints("done")
prints("__finalize")


class ProjectScanner extends Node:

class SourceScanner extends Node:

enum {
INIT,
SCAN,
QUIT
QUIT,
DONE
}


var _counter = 0
var WAIT_TIME_IN_MS = 5.000
var _state = INIT
Expand All @@ -46,7 +46,9 @@ class ProjectScanner extends Node:
_console.prints_color("Running project scan:", Color.CORNFLOWER_BLUE)
await scan_project()
set_process(true)
_state = QUIT
if _state == QUIT or _counter >= WAIT_TIME_IN_MS:
_state = DONE
_console.prints_color("Scan project done.", Color.CORNFLOWER_BLUE)
_console.prints_color("======================================", Color.CORNFLOWER_BLUE)
_console.new_line()
Expand All @@ -58,24 +60,31 @@ class ProjectScanner extends Node:
var plugin := EditorPlugin.new()
var fs := plugin.get_editor_interface().get_resource_filesystem()

_console.prints_color("Scan :", Color.SANDY_BROWN)
_console.progressBar(0)
fs.scan()
await get_tree().process_frame
while fs.is_scanning():
await get_tree().process_frame
_console.progressBar(fs.get_scanning_progress() * 100 as int)
_console.progressBar(100)
_console.new_line()

if fs.has_method("reimport_files--"):
_console.prints_color("Reimport images :", Color.SANDY_BROWN)
for source in ["res://addons/gdUnit4/src/ui/assets/orphan", "res://addons/gdUnit4/src/ui/assets/spinner", "res://addons/gdUnit4/src/ui/assets/"]:
var image_files := Array(DirAccess.get_files_at(source))
#_console.prints_color("%s" % image_files, Color.SANDY_BROWN)
var files := image_files.map(func full_path(file_name):
return "%s/%s" % [source, file_name] )\
.filter(func filter_import_files(path :String):
return path.get_extension() != "import")
prints(files)
fs.reimport_files(files)

_console.prints_color("Scan sources: ", Color.SANDY_BROWN)
_console.progressBar(0)
fs.scan_sources()
await get_tree().create_timer(5).timeout
await get_tree().process_frame

_console.prints_color("Scan: ", Color.SANDY_BROWN)
fs.scan()
await get_tree().process_frame
while fs.is_scanning():
await get_tree().process_frame
_console.progressBar(fs.get_scanning_progress() * 100 as int)
_console.progressBar(100)
_console.new_line()
plugin.free()
_state = QUIT
await get_tree().process_frame
plugin.queue_free()
await get_tree().process_frame
2 changes: 2 additions & 0 deletions addons/gdUnit4/plugin.gd
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ func _enter_tree():
if GdUnitSettings.is_update_notification_enabled():
var update_tool = load("res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn").instantiate()
Engine.get_main_loop().root.call_deferred("add_child", update_tool)
if GdUnit4MonoApiLoader.is_mono_supported():
prints("GdUnit4Mono Version %s loaded." % GdUnit4MonoApiLoader.version())


func _exit_tree():
Expand Down
2 changes: 1 addition & 1 deletion addons/gdUnit4/runtest.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ IF "%GODOT_TYPE%" == "mono" (

%GODOT_BIN% -s -d .\addons\gdUnit4\bin\GdUnitCmdTool.gd %*
SET exit_code=%errorlevel%
%GODOT_BIN% --no-window --quiet -s -d .\addons\gdUnit4\bin\GdUnitCopyLog.gd %*
%GODOT_BIN% --headless --quiet -s -d .\addons\gdUnit4\bin\GdUnitCopyLog.gd %*

ECHO %exit_code%

Expand Down
10 changes: 5 additions & 5 deletions addons/gdUnit4/src/GdUnitAwaiter.gd
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const GdUnitAssertImpl = preload("res://addons/gdUnit4/src/asserts/GdUnitAssertI
# signal_name: signal name
# args: the expected signal arguments as an array
# timeout: the timeout in ms, default is set to 2000ms
static func await_signal_on(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:
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 line_number := GdUnitAssert._get_line_number()
if not is_instance_valid(source):
Expand All @@ -34,7 +34,7 @@ static func await_signal_on(source :Object, signal_name :String, args :Array = [
# signal_name: signal name
# args: the expected signal arguments as an array
# timeout: the timeout in ms, default is set to 2000ms
static func await_signal_idle_frames(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:
func await_signal_idle_frames(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:
var line_number := GdUnitAssert._get_line_number()
# fail fast if the given source instance invalid
if not is_instance_valid(source):
Expand All @@ -54,17 +54,17 @@ static func await_signal_idle_frames(source :Object, signal_name :String, args :
# # waits for 100ms
# await GdUnitAwaiter.await_millis(myNode, 100).completed
# use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out
static func await_millis(milliSec :int) -> void:
func await_millis(milliSec :int) -> void:
var timer :Timer = Timer.new()
timer.set_name("gdunit_await_millis_timer_%d" % timer.get_instance_id())
Engine.get_main_loop().root.add_child(timer)
timer.add_to_group("GdUnitTimers")
timer.set_one_shot(true)
timer.start(milliSec * 0.001)
timer.start(milliSec / 1000.0)
await timer.timeout
timer.queue_free()


# Waits until the next idle frame
static func await_idle_frame() -> void:
func await_idle_frame() -> void:
await Engine.get_main_loop().process_frame
31 changes: 20 additions & 11 deletions addons/gdUnit4/src/GdUnitTestSuite.gd
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ const NO_ARG :Variant = GdUnitConstants.NO_ARG
var __is_skipped := false
@warning_ignore("unused_private_class_variable")
var __skip_reason :String = "Unknow."
var __active_test_case :String
var __awaiter := __gdunit_awaiter()
# holds the actual execution context
var __execution_context


### We now load all used asserts and tool scripts into the cache according to the principle of "lazy loading"
### in order to noticeably reduce the loading time of the test suite.
# We go this hard way to increase the loading performance to avoid reparsing all the used scripts
# for more detailed info -> https://github.com/godotengine/godot/issues/67400
func __lazy_load(script_path :String) -> GDScript:
return ResourceLoader.load(script_path, "GDScript", ResourceLoader.CACHE_MODE_REUSE)
return GdUnitAssertions.__lazy_load(script_path)


func __gdunit_assert() -> GDScript:
Expand All @@ -39,8 +43,8 @@ func __gdunit_tools() -> GDScript:
return __lazy_load("res://addons/gdUnit4/src/core/GdUnitTools.gd")


func __gdunit_awaiter() -> GDScript:
return __lazy_load("res://addons/gdUnit4/src/GdUnitAwaiter.gd")
func __gdunit_awaiter() -> Object:
return __lazy_load("res://addons/gdUnit4/src/GdUnitAwaiter.gd").new()


func __gdunit_argument_matchers():
Expand Down Expand Up @@ -79,7 +83,6 @@ func is_failure(_expected_failure :String = NO_ARG) -> bool:
return Engine.get_meta("GD_TEST_FAILURE") if Engine.has_meta("GD_TEST_FAILURE") else false


var __active_test_case :String
func set_active_test_case(test_case :String) -> void:
__active_test_case = test_case

Expand All @@ -93,8 +96,14 @@ func error_as_string(error_number :int) -> String:

## A litle helper to auto freeing your created objects after test execution
func auto_free(obj) -> Variant:
var GdUnitMemoryPool = __lazy_load("res://addons/gdUnit4/src/core/GdUnitMemoryPool.gd")
return GdUnitMemoryPool.register_auto_free(obj, get_meta(GdUnitMemoryPool.META_PARAM))
return __execution_context.register_auto_free(obj)


@warning_ignore("native_method_override")
func add_child(node :Node, force_readable_name := false, internal := Node.INTERNAL_MODE_DISABLED) -> void:
super.add_child(node, force_readable_name, internal)
if __execution_context != null:
__execution_context.orphan_monitor_start()


## Discard the error message triggered by a timeout (interruption).[br]
Expand Down Expand Up @@ -151,12 +160,12 @@ func clear_push_errors() -> void:
## args: the expected signal arguments as an array[br]
## timeout: the timeout in ms, default is set to 2000ms
func await_signal_on(source :Object, signal_name :String, args :Array = [], timeout :int = 2000) -> Variant:
return await __gdunit_awaiter().await_signal_on(source, signal_name, args, timeout)
return await __awaiter.await_signal_on(source, signal_name, args, timeout)


## Waits until the next idle frame
func await_idle_frame():
await __gdunit_awaiter().await_idle_frame()
await __awaiter.await_idle_frame()


## Waits for for a given amount of milliseconds[br]
Expand All @@ -167,7 +176,7 @@ func await_idle_frame():
## [/codeblock][br]
## use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out
func await_millis(timeout :int):
await __gdunit_awaiter().await_millis(timeout)
await __awaiter.await_millis(timeout)


## Creates a new scene runner to allow simulate interactions checked a scene.[br]
Expand Down Expand Up @@ -198,12 +207,12 @@ const RETURN_DEEP_STUB = GdUnitMock.RETURN_DEEP_STUB

## Creates a mock for given class name
func mock(clazz, mock_mode := RETURN_DEFAULTS) -> Object:
return __lazy_load("res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd").build(self, clazz, mock_mode)
return __lazy_load("res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd").build(clazz, mock_mode)


## Creates a spy checked given object instance
func spy(instance) -> Object:
return __lazy_load("res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd").build(self, instance)
return __lazy_load("res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd").build(instance)


## Configures a return value for the specified function and used arguments.[br]
Expand Down
Loading

0 comments on commit a0376f7

Please sign in to comment.