From 1f70bfb80ab70b27067fc1c6178ec5e6b024d495 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Mon, 6 Apr 2026 22:29:08 -0400 Subject: [PATCH] NS-21: track GdUnit4 bin/ scripts (unignore vs root bin/ rule) Root .gitignore bin/ matched client/addons/gdUnit4/bin/, so CI never had GdUnitCmdTool.gd. Negate that path; add the four bin files. --- .gitignore | 3 + client/addons/gdUnit4/bin/GdUnitCmdTool.gd | 21 +++ .../addons/gdUnit4/bin/GdUnitCmdTool.gd.uid | 1 + client/addons/gdUnit4/bin/GdUnitCopyLog.gd | 167 ++++++++++++++++++ .../addons/gdUnit4/bin/GdUnitCopyLog.gd.uid | 1 + 5 files changed, 193 insertions(+) create mode 100644 client/addons/gdUnit4/bin/GdUnitCmdTool.gd create mode 100644 client/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid create mode 100644 client/addons/gdUnit4/bin/GdUnitCopyLog.gd create mode 100644 client/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid diff --git a/.gitignore b/.gitignore index cb31bf0..c74f550 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # .NET bin/ +# Allow GdUnit4 CLI scripts (path contains …/gdUnit4/bin/, not build output). +!client/addons/gdUnit4/bin/ +!client/addons/gdUnit4/bin/** obj/ out/ *.user diff --git a/client/addons/gdUnit4/bin/GdUnitCmdTool.gd b/client/addons/gdUnit4/bin/GdUnitCmdTool.gd new file mode 100644 index 0000000..cae9138 --- /dev/null +++ b/client/addons/gdUnit4/bin/GdUnitCmdTool.gd @@ -0,0 +1,21 @@ +#!/usr/bin/env -S godot -s +extends SceneTree + + +var _cli_runner: GdUnitTestCIRunner + + +func _initialize() -> void: + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) + _cli_runner = GdUnitTestCIRunner.new() + root.add_child(_cli_runner) + + +# do not use print statements on _finalize it results in random crashes +func _finalize() -> void: + queue_delete(_cli_runner) + if OS.is_stdout_verbose(): + prints("Finallize ..") + prints("-Orphan nodes report-----------------------") + Window.print_orphan_nodes() + prints("Finallize .. done") diff --git a/client/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid b/client/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid new file mode 100644 index 0000000..74e82e2 --- /dev/null +++ b/client/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid @@ -0,0 +1 @@ +uid://dy0y43t1ir3sq diff --git a/client/addons/gdUnit4/bin/GdUnitCopyLog.gd b/client/addons/gdUnit4/bin/GdUnitCopyLog.gd new file mode 100644 index 0000000..8b22805 --- /dev/null +++ b/client/addons/gdUnit4/bin/GdUnitCopyLog.gd @@ -0,0 +1,167 @@ +#!/usr/bin/env -S godot -s +extends MainLoop + +const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") + +# gdlint: disable=max-line-length +const LOG_FRAME_TEMPLATE = """ + + + + + + Godot Logging + + + + +
+${content} +
+ + +""" + +const NO_LOG_MESSAGE = """ +

No logging available!

+
+

In order for logging to take place, you must activate the Activate file logging option in the project settings.

+

You can enable the logging under: +Project Settings > Debug > File Logging > Enable File Logging in the project settings.

+""" + +#warning-ignore-all:return_value_discarded +var _cmd_options := CmdOptions.new([ + CmdOption.new( + "-rd, --report-directory", + "-rd ", + "Specifies the output directory in which the reports are to be written. The default is res://reports/.", + TYPE_STRING, + true + ) + ]) + + +var _report_root_path: String +var _current_report_path: String +var _debug_cmd_args := PackedStringArray() + + +func _init() -> void: + set_report_directory(GdUnitFileAccess.current_dir() + "reports") + set_current_report_path() + + +func _process(_delta: float) -> bool: + # check if reports exists + if not reports_available(): + prints("no reports found") + return true + + # only process if godot logging is enabled + if not GdUnitSettings.is_log_enabled(): + write_report(NO_LOG_MESSAGE, "") + return true + + # parse possible custom report path, + var cmd_parser := CmdArgumentParser.new(_cmd_options, "GdUnitCmdTool.gd") + # ignore erros and exit quitly + if cmd_parser.parse(get_cmdline_args(), true).is_error(): + return true + CmdCommandHandler.new(_cmd_options).register_cb("-rd", set_report_directory) + + var godot_log_file := scan_latest_godot_log() + var result := read_log_file_content(godot_log_file) + if result.is_error(): + write_report(result.error_message(), godot_log_file) + return true + write_report(result.value_as_string(), godot_log_file) + return true + + +func set_current_report_path() -> void: + # scan for latest report directory + var iteration := GdUnitFileAccess.find_last_path_index( + _report_root_path, GdUnitConstants.REPORT_DIR_PREFIX + ) + _current_report_path = "%s/%s%d" % [_report_root_path, GdUnitConstants.REPORT_DIR_PREFIX, iteration] + + +func set_report_directory(path: String) -> void: + _report_root_path = path + + +func get_log_report_html() -> String: + return _current_report_path + "/godot_report_log.html" + + +func reports_available() -> bool: + return DirAccess.dir_exists_absolute(_report_root_path) + + +func scan_latest_godot_log() -> String: + var path := GdUnitSettings.get_log_path().get_base_dir() + var files_sorted := Array() + for file in GdUnitFileAccess.scan_dir(path): + var file_name := "%s/%s" % [path, file] + files_sorted.append(file_name) + # sort by name, the name contains the timestamp so we sort at the end by timestamp + files_sorted.sort() + return files_sorted.back() + + +func read_log_file_content(log_file: String) -> GdUnitResult: + var file := FileAccess.open(log_file, FileAccess.READ) + if file == null: + return GdUnitResult.error( + "Can't find log file '%s'. Error: %s" + % [log_file, error_string(FileAccess.get_open_error())] + ) + var content := "
" + file.get_as_text()
+	# patch out console format codes
+	for color_index in range(0, 256):
+		var to_replace := "[38;5;%dm" % color_index
+		content = content.replace(to_replace, "")
+	content += "
" + content = content\ + .replace("", "")\ + .replace(GdUnitCSIMessageWriter.CSI_BOLD, "")\ + .replace(GdUnitCSIMessageWriter.CSI_ITALIC, "")\ + .replace(GdUnitCSIMessageWriter.CSI_UNDERLINE, "") + return GdUnitResult.success(content) + + +func write_report(content: String, godot_log_file: String) -> GdUnitResult: + var file := FileAccess.open(get_log_report_html(), FileAccess.WRITE) + if file == null: + return GdUnitResult.error( + "Can't open to write '%s'. Error: %s" + % [get_log_report_html(), error_string(FileAccess.get_open_error())] + ) + var report_html := LOG_FRAME_TEMPLATE.replace("${content}", content) + file.store_string(report_html) + _update_index_html(godot_log_file) + return GdUnitResult.success(file) + + +func _update_index_html(godot_log_file: String) -> void: + var index_path := "%s/index.html" % _current_report_path + var index_file := FileAccess.open(index_path, FileAccess.READ_WRITE) + if index_file == null: + push_error( + "Can't add log path '%s' to `%s`. Error: %s" + % [godot_log_file, index_path, error_string(FileAccess.get_open_error())] + ) + return + var content := index_file.get_as_text()\ + .replace("${log_report}", get_log_report_html())\ + .replace("${godot_log_file}", godot_log_file) + # overide it + index_file.seek(0) + index_file.store_string(content) + + +func get_cmdline_args() -> PackedStringArray: + if _debug_cmd_args.is_empty(): + return OS.get_cmdline_args() + return _debug_cmd_args diff --git a/client/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid b/client/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid new file mode 100644 index 0000000..e8f4d7b --- /dev/null +++ b/client/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid @@ -0,0 +1 @@ +uid://blqb428xny7rq