From d16ba33cfb255fef9f3eeb1e2bbea996e38d756d Mon Sep 17 00:00:00 2001 From: Nullpointer Date: Sat, 14 Sep 2024 12:17:34 +0200 Subject: [PATCH] GD-566: Update HTML report page look & feel (#567) # Why https://github.com/MikeSchulze/gdUnit4/issues/566 # What - restyle the look and feel - update the styles ![image](https://github.com/user-attachments/assets/23d521a6-2584-44f4-9005-d892b0ed5161) ![image](https://github.com/user-attachments/assets/cfc2313d-607b-42ba-a6f4-578ad197326b) ![image](https://github.com/user-attachments/assets/0781e15c-c9e8-49e9-bf68-946e1a52fdea) ![image](https://github.com/user-attachments/assets/94e82b7e-c37b-4464-ba22-2b836af2f0dd) --- addons/gdUnit4/bin/GdUnitCopyLog.gd | 151 +++--- .../gdUnit4/src/report/GdUnitByPathReport.gd | 21 +- .../gdUnit4/src/report/GdUnitHtmlPatterns.gd | 90 +++- .../gdUnit4/src/report/GdUnitReportSummary.gd | 26 +- .../src/report/GdUnitTestCaseReport.gd | 3 +- .../src/report/GdUnitTestSuiteReport.gd | 10 +- .../src/report/template/css/breadcrumb.css | 29 +- .../gdUnit4/src/report/template/css/icon.png | Bin 13817 -> 0 bytes .../gdUnit4/src/report/template/css/logo.png | Bin 0 -> 49775 bytes .../css/{icon.png.import => logo.png.import} | 9 +- .../gdUnit4/src/report/template/css/style.css | 327 ------------ .../src/report/template/css/styles.css | 468 ++++++++++++++++++ .../src/report/template/folder_report.html | 176 ++++--- addons/gdUnit4/src/report/template/index.html | 206 ++++---- .../src/report/template/suite_report.html | 205 +++++--- addons/gdUnit4/test/cmd/runtestDebug.tscn | 11 +- addons/gdUnit4/test/core/flaky_test.gd | 156 ++++++ 17 files changed, 1198 insertions(+), 690 deletions(-) delete mode 100644 addons/gdUnit4/src/report/template/css/icon.png create mode 100644 addons/gdUnit4/src/report/template/css/logo.png rename addons/gdUnit4/src/report/template/css/{icon.png.import => logo.png.import} (65%) delete mode 100644 addons/gdUnit4/src/report/template/css/style.css create mode 100644 addons/gdUnit4/src/report/template/css/styles.css create mode 100644 addons/gdUnit4/test/core/flaky_test.gd diff --git a/addons/gdUnit4/bin/GdUnitCopyLog.gd b/addons/gdUnit4/bin/GdUnitCopyLog.gd index 2e037a69..8e4067fa 100644 --- a/addons/gdUnit4/bin/GdUnitCopyLog.gd +++ b/addons/gdUnit4/bin/GdUnitCopyLog.gd @@ -4,23 +4,30 @@ extends MainLoop const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") # gdlint: disable=max-line-length -const NO_LOG_TEMPLATE = """ +const LOG_FRAME_TEMPLATE = """ - + - - - Logging - + + + Godot Logging + - -
-

No logging available!

-
-

For logging to occur, you must check Enable File Logging in Project Settings.

-

You can enable Logging Project Settings > Logging > File Logging > Enable File Logging in the Project Settings.

+ + +
+${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 @@ -34,48 +41,65 @@ var _cmd_options := CmdOptions.new([ ) ]) + var _report_root_path: String +var _current_report_path: String +var _debug_cmd_args := PackedStringArray() func _init() -> void: - _report_root_path = GdUnitFileAccess.current_dir() + "reports" + set_report_directory(GdUnitFileAccess.current_dir() + "reports") + set_current_report_path() -func _process(_delta :float) -> bool: +func _process(_delta: float) -> bool: # check if reports exists if not reports_available(): prints("no reports found") return true - # scan for latest report path - var iteration := GdUnitFileAccess.find_last_path_index( - _report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX - ) - var report_path := "%s/%s%d" % [_report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX, iteration] + # only process if godot logging is enabled if not GdUnitSettings.is_log_enabled(): - _patch_report(report_path, "") + 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(OS.get_cmdline_args(), true).is_error(): + if cmd_parser.parse(get_cmdline_args(), true).is_error(): return true CmdCommandHandler.new(_cmd_options).register_cb("-rd", set_report_directory) - # scan for latest godot log and copy to report - var godot_log := _scan_latest_godot_log() - var result := _copy_and_pach(godot_log, report_path) + + var godot_log_file := scan_latest_godot_log() + var result := read_log_file_content(godot_log_file) if result.is_error(): - push_error(result.error_message()) + write_report(result.error_message(), godot_log_file) return true - _patch_report(report_path, godot_log) + write_report(result.value(), 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, GdUnitHtmlReport.REPORT_DIR_PREFIX + ) + _current_report_path = "%s/%s%d" % [_report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX, iteration] + + func set_report_directory(path: String) -> void: _report_root_path = path -func _scan_latest_godot_log() -> String: +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): @@ -83,59 +107,60 @@ func _scan_latest_godot_log() -> String: 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[-1] - - -func _patch_report(report_path: String, godot_log: String) -> void: - var index_file := FileAccess.open("%s/index.html" % report_path, FileAccess.READ_WRITE) - if index_file == null: - push_error( - "Can't add log path to index.html. Error: %s" - % error_string(FileAccess.get_open_error()) - ) - return - # if no log file available than add a information howto enable it - if godot_log.is_empty(): - FileAccess.open( - "%s/logging_not_available.html" % report_path, - FileAccess.WRITE).store_string(NO_LOG_TEMPLATE) - var log_file := "logging_not_available.html" if godot_log.is_empty() else godot_log.get_file() - var content := index_file.get_as_text().replace("${log_file}", log_file) - # overide it - index_file.seek(0) - index_file.store_string(content) + return files_sorted.back() -func _copy_and_pach(from_file: String, to_dir: String) -> GdUnitResult: - var result := GdUnitFileAccess.copy_file(from_file, to_dir) - if result.is_error(): - return result - var file := FileAccess.open(from_file, FileAccess.READ) +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 file '%s'. Error: %s" - % [from_file, error_string(FileAccess.get_open_error())] + "Can't find log file '%s'. Error: %s" + % [log_file, error_string(FileAccess.get_open_error())] ) - var content := file.get_as_text() + 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(CmdConsole.CSI_BOLD, "")\ .replace(CmdConsole.CSI_ITALIC, "")\ .replace(CmdConsole.CSI_UNDERLINE, "") - var to_file := to_dir + "/" + from_file.get_file() - file = FileAccess.open(to_file, FileAccess.WRITE) + 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" - % [to_file, error_string(FileAccess.get_open_error())] + % [get_log_report_html(), error_string(FileAccess.get_open_error())] ) - file.store_string(content) - return GdUnitResult.empty() + 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 reports_available() -> bool: - return DirAccess.dir_exists_absolute(_report_root_path) +func _update_index_html(godot_log_file: String) -> void: + var index_file := FileAccess.open("%s/index.html" % _current_report_path, FileAccess.READ_WRITE) + if index_file == null: + push_error( + "Can't add log path to index.html. Error: %s" + % 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/addons/gdUnit4/src/report/GdUnitByPathReport.gd b/addons/gdUnit4/src/report/GdUnitByPathReport.gd index 34d246ab..7e5e953c 100644 --- a/addons/gdUnit4/src/report/GdUnitByPathReport.gd +++ b/addons/gdUnit4/src/report/GdUnitByPathReport.gd @@ -11,7 +11,7 @@ func _init(p_path :String, report_summaries :Array[GdUnitReportSummary]) -> void static func sort_reports_by_path(report_summaries :Array[GdUnitReportSummary]) -> Dictionary: var by_path := Dictionary() for report in report_summaries: - var suite_path :String = report.path() + var suite_path :String = ProjectSettings.localize_path(report.path()) var suite_report :Array[GdUnitReportSummary] = by_path.get(suite_path, [] as Array[GdUnitReportSummary]) suite_report.append(report) by_path[suite_path] = suite_report @@ -27,6 +27,7 @@ func create_record(report_link :String) -> String: func write(report_dir :String) -> String: + calculate_summary() var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/report/template/folder_report.html") var path_report := GdUnitHtmlPatterns.build(template, self, "") path_report = apply_testsuite_reports(report_dir, path_report, _reports) @@ -41,9 +42,17 @@ func write(report_dir :String) -> String: func apply_testsuite_reports(report_dir :String, template :String, test_suite_reports :Array[GdUnitReportSummary]) -> String: var table_records := PackedStringArray() - for report in test_suite_reports: - if report is GdUnitTestSuiteReport: - var test_suite_report := report as GdUnitTestSuiteReport - var report_link := test_suite_report.output_path(report_dir).replace(report_dir, "..") - table_records.append(test_suite_report.create_record(report_link)) + for report:GdUnitTestSuiteReport in test_suite_reports: + var report_link := report.output_path(report_dir).replace(report_dir, "..") + table_records.append(report.create_record(report_link)) return template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, "\n".join(table_records)) + + +func calculate_summary() -> void: + for report:GdUnitTestSuiteReport in get_reports(): + _error_count += report.error_count() + _failure_count += report.failure_count() + _orphan_count += report.orphan_count() + _skipped_count += report.skipped_count() + _flaky_count += report.flaky_count() + _duration += report.duration() diff --git a/addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd b/addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd index 7a70c750..722c528f 100644 --- a/addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd +++ b/addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd @@ -2,50 +2,77 @@ class_name GdUnitHtmlPatterns extends RefCounted const TABLE_RECORD_TESTSUITE = """ - - ${testsuite_name} + + ${testsuite_name} + ${report_state_label} ${test_count} ${skipped_count} ${flaky_count} ${failure_count} ${orphan_count} ${duration} - ${success_percent} + +
+
+
+
+
+
+
+ """ const TABLE_RECORD_PATH = """ - + ${path} + ${report_state_label} ${test_count} ${skipped_count} ${flaky_count} ${failure_count} ${orphan_count} ${duration} - ${success_percent} + +
+
+
+
+
+
+
+ """ const TABLE_REPORT_TESTSUITE = """ - - TestSuite hooks + + TestSuite hooks n/a ${orphan_count} ${duration} - ${failure-report} + +
+${failure-report}
+										
+ """ const TABLE_RECORD_TESTCASE = """ - - ${testcase_name} + + ${testcase_name} + ${report_state_label} ${skipped_count} ${orphan_count} ${duration} - ${failure-report} + +
+${failure-report}
+										
+ """ @@ -55,7 +82,9 @@ const TABLE_BY_TESTCASES = "${report_table_tests}" # the report state success, error, warning const REPORT_STATE = "${report_state}" +const REPORT_STATE_LABEL = "${report_state_label}" const PATH = "${path}" +const RESOURCE_PATH = "${resource_path}" const TESTSUITE_COUNT = "${suite_count}" const TESTCASE_COUNT = "${test_count}" const FAILURE_COUNT = "${failure_count}" @@ -66,6 +95,12 @@ const DURATION = "${duration}" const FAILURE_REPORT = "${failure-report}" const SUCCESS_PERCENT = "${success_percent}" +const QUICK_STATE_PASSED = "${passed-percent}" +const QUICK_STATE_FLAKY = "${flaky-percent}" +const QUICK_STATE_ERROR = "${error-percent}" +const QUICK_STATE_FAILED = "${failed-percent}" +const QUICK_STATE_WARNING = "${warning-percent}" + const TESTSUITE_NAME = "${testsuite_name}" const TESTCASE_NAME = "${testcase_name}" const REPORT_LINK = "${report_link}" @@ -77,10 +112,12 @@ static func current_date() -> String: return Time.get_datetime_string_from_system(true, true) -static func build(template :String, report :GdUnitReportSummary, report_link :String) -> String: +static func build(template: String, report: GdUnitReportSummary, report_link: String) -> String: return template\ - .replace(PATH, report.path())\ - .replace(TESTSUITE_NAME, report.name())\ + .replace(PATH, get_report_path(report))\ + .replace(BREADCRUMP_PATH_LINK, get_path_as_link(report))\ + .replace(RESOURCE_PATH, report.resource_path())\ + .replace(TESTSUITE_NAME, report.name_html_encoded())\ .replace(TESTSUITE_COUNT, str(report.suite_count()))\ .replace(TESTCASE_COUNT, str(report.test_count()))\ .replace(FAILURE_COUNT, str(report.error_count() + report.failure_count()))\ @@ -89,10 +126,33 @@ static func build(template :String, report :GdUnitReportSummary, report_link :St .replace(ORPHAN_COUNT, str(report.orphan_count()))\ .replace(DURATION, LocalTime.elapsed(report.duration()))\ .replace(SUCCESS_PERCENT, report.calculate_succes_rate(report.test_count(), report.error_count(), report.failure_count()))\ - .replace(REPORT_STATE, report.report_state())\ + .replace(REPORT_STATE, report.report_state().to_lower())\ + .replace(REPORT_STATE_LABEL, report.report_state())\ + .replace(QUICK_STATE_PASSED, calculate_percentage(report.test_count(), report.success_count()))\ + .replace(QUICK_STATE_FLAKY, calculate_percentage(report.test_count(), report.flaky_count()))\ + .replace(QUICK_STATE_ERROR, calculate_percentage(report.test_count(), report.error_count()))\ + .replace(QUICK_STATE_FAILED, calculate_percentage(report.test_count(), report.failure_count()))\ + .replace(QUICK_STATE_WARNING, calculate_percentage(report.test_count(), 0))\ .replace(REPORT_LINK, report_link)\ .replace(BUILD_DATE, current_date()) static func load_template(template_name :String) -> String: return FileAccess.open(template_name, FileAccess.READ).get_as_text() + + +static func get_path_as_link(report: GdUnitReportSummary) -> String: + return "../path/%s.html" % report.path().replace("/", ".") + + +static func get_report_path(report: GdUnitReportSummary) -> String: + var path := report.path() + if path.is_empty(): + return "/" + return path + + +static func calculate_percentage(p_test_count: int, count: int) -> String: + if count <= 0: + return "0%" + return "%d" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + "%" diff --git a/addons/gdUnit4/src/report/GdUnitReportSummary.gd b/addons/gdUnit4/src/report/GdUnitReportSummary.gd index a26df64b..1c29e8b8 100644 --- a/addons/gdUnit4/src/report/GdUnitReportSummary.gd +++ b/addons/gdUnit4/src/report/GdUnitReportSummary.gd @@ -19,8 +19,11 @@ var _flaky_count := 0 var _duration := 0 var _reports :Array[GdUnitReportSummary] = [] - func name() -> String: + return _name + + +func name_html_encoded() -> String: return html_encode(_name) @@ -55,6 +58,10 @@ func test_executed_count() -> int: return test_count() - skipped_count() +func success_count() -> int: + return test_count() - error_count() - failure_count() - flaky_count() + + func error_count() -> int: return _error_count @@ -97,14 +104,14 @@ func succes_rate() -> String: func calculate_state(p_error_count :int, p_failure_count :int, p_orphan_count :int, p_flaky_count: int) -> String: if p_error_count > 0: - return "error" + return "ERROR" if p_failure_count > 0: - return "failure" + return "FAILED" if p_flaky_count > 0: - return "flaky" + return "FLAKY" if p_orphan_count > 0: - return "warning" - return "success" + return "WARNING" + return "PASSED" func calculate_succes_rate(p_test_count :int, p_error_count :int, p_failure_count :int) -> String: @@ -127,9 +134,4 @@ func html_encode(value :String) -> String: func convert_rtf_to_html(bbcode :String) -> String: - var as_text: = GdUnitTools.richtext_normalize(bbcode) - var converted := PackedStringArray() - var lines := as_text.split("\n") - for line in lines: - converted.append("

%s

" % line) - return "\n".join(converted) + return GdUnitTools.richtext_normalize(bbcode) diff --git a/addons/gdUnit4/src/report/GdUnitTestCaseReport.gd b/addons/gdUnit4/src/report/GdUnitTestCaseReport.gd index d5ad453f..5eec799e 100644 --- a/addons/gdUnit4/src/report/GdUnitTestCaseReport.gd +++ b/addons/gdUnit4/src/report/GdUnitTestCaseReport.gd @@ -25,7 +25,8 @@ func failure_report() -> String: func create_record(_report_dir :String) -> String: return GdUnitHtmlPatterns.TABLE_RECORD_TESTCASE\ - .replace(GdUnitHtmlPatterns.REPORT_STATE, report_state())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE, report_state().to_lower())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report_state())\ .replace(GdUnitHtmlPatterns.TESTCASE_NAME, name())\ .replace(GdUnitHtmlPatterns.SKIPPED_COUNT, str(skipped_count()))\ .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(orphan_count()))\ diff --git a/addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd b/addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd index f200061c..23897dbf 100644 --- a/addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd +++ b/addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd @@ -20,10 +20,6 @@ func output_path(report_dir :String) -> String: return "%s/test_suites/%s.%s.html" % [report_dir, path().replace("/", "."), name()] -func path_as_link() -> String: - return "../path/%s.html" % path().replace("/", ".") - - func failure_report() -> String: var html_report := "" for report in _failure_reports: @@ -33,7 +29,8 @@ func failure_report() -> String: func test_suite_failure_report() -> String: return GdUnitHtmlPatterns.TABLE_REPORT_TESTSUITE\ - .replace(GdUnitHtmlPatterns.REPORT_STATE, report_state())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE, report_state().to_lower())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report_state())\ .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(orphan_count()))\ .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(_duration))\ .replace(GdUnitHtmlPatterns.FAILURE_REPORT, failure_report()) @@ -41,8 +38,7 @@ func test_suite_failure_report() -> String: func write(report_dir :String) -> String: var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/report/template/suite_report.html") - template = GdUnitHtmlPatterns.build(template, self, "")\ - .replace(GdUnitHtmlPatterns.BREADCRUMP_PATH_LINK, path_as_link()) + template = GdUnitHtmlPatterns.build(template, self, "") var report_output_path := output_path(report_dir) var test_report_table := PackedStringArray() diff --git a/addons/gdUnit4/src/report/template/css/breadcrumb.css b/addons/gdUnit4/src/report/template/css/breadcrumb.css index 2dd65fee..17215ff2 100644 --- a/addons/gdUnit4/src/report/template/css/breadcrumb.css +++ b/addons/gdUnit4/src/report/template/css/breadcrumb.css @@ -1,15 +1,12 @@ - .breadcrumb { display: flex; border-radius: 6px; overflow: hidden; height: 45px; z-index: 1; - background-color: #055d9c; - font-weight: bold; - font-size: 16px; + background-color: #9d73eb; margin-top: 0px; - margin-bottom: 20px; + margin-bottom: 10px; box-shadow: 0 0 3px black; } @@ -25,11 +22,11 @@ } .breadcrumb a:first-child { - padding-left: 5.2px; + padding-left: 5.2px; } .breadcrumb a:last-child { - padding-right: 5.2px; + padding-right: 5.2px; } .breadcrumb a:after { @@ -40,28 +37,30 @@ height: 45px; top: 0; right: -20px; - background-color: #055d9c; + background-color: #9d73eb; border-top-right-radius: 5px; transform: scale(0.707) rotate(45deg); - box-shadow: 2px -2px rgba(0,0,0,0.25); + box-shadow: 2px -2px rgba(0, 0, 0, 0.25); z-index: 1; } .breadcrumb a:last-child:after { - content: none; + content: none; } -.breadcrumb a.active, .breadcrumb a:hover { - background: #347bad; +.breadcrumb a.active, +.breadcrumb a:hover { + background: #b899f2; color: white; text-decoration: underline; } -.breadcrumb a.active:after, .breadcrumb a:hover:after { - background: #347bad; +.breadcrumb a.active:after, +.breadcrumb a:hover:after { + background: #b899f2; } .breadcrumb span { - margin:inherit; + margin: inherit; z-index: 2; } diff --git a/addons/gdUnit4/src/report/template/css/icon.png b/addons/gdUnit4/src/report/template/css/icon.png deleted file mode 100644 index eeac2924071bb62e97d1f372ca66c0e7e1a256f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13817 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%0AKr(9Uc>BSL)Bn(%Dc$$Ky(%WMe% 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 + - - - Testsuite Results - - + + + + + GdUnit4 Testsuite + + - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
TestSuites${suite_count}
Tests${test_count}
Skipped${skipped_count}
Failures${failure_count}
Orphans${orphan_count}
Duration${duration}
-
-
-

Success Rate

- - - - -
${success_percent}
+
+ +
+

Report by Paths

+
+ ${resource_path}
-
-

History

-

Coming Next

+
+
+ TestSuites + ${suite_count} +
+
+ Tests + ${test_count} +
+
+ Skipped + ${skipped_count} +
+
+ Flaky + ${flaky_count} +
+
+ Failures + ${failure_count} +
+
+ Orphans + ${orphan_count} +
+
+ Duration + ${duration} +
+
+
+
+ Success Rate + ${success_percent} +
+
- +
+
-
- - - -
-
- - - - - - - - - - - - - - ${report_table_testsuites} - -
TestsSkippedFailuresOrphansDurationSuccess rate
-
-
+
+
+ + + + + + + + + + + + + + + + ${report_table_testsuites} + +
TestSuitesResultTestsSkippedFlakyFailuresOrphansDurationSuccess rate
+
-
+
-

Generated byGdUnit4 at ${buid_date}

+

Generated by GdUnit4 at ${buid_date}

+
+ + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
- + diff --git a/addons/gdUnit4/src/report/template/index.html b/addons/gdUnit4/src/report/template/index.html index 50f2cb5f..1c0e7694 100644 --- a/addons/gdUnit4/src/report/template/index.html +++ b/addons/gdUnit4/src/report/template/index.html @@ -1,133 +1,161 @@ - + - - - Report Summary - + + + GdUnit4 Report + -
-

GdUnit4 Report

-
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TestSuites${suite_count}
Tests${test_count}
Skipped${skipped_count}
Flaky${flaky_count}
Failures${failure_count}
Orphans${orphan_count}
Duration${duration}
-
-
-

Success Rate

- - - - -
${success_percent}
-
-
-

History

-

Coming Next

+
+ +
+

Summary Report

+
+
+ Test Suites + ${suite_count} +
+
+ Tests + ${test_count} +
+
+ Skipped + ${skipped_count} +
+
+ Flaky + ${flaky_count} +
+
+ Failures + ${failure_count} +
+
+ Orphans + ${orphan_count} +
+
+ Duration + ${duration} +
+
+
+
+ Success Rate + ${success_percent} +
+
+
+
+ +
+ +
+
-
-

Reports

-
- - - - - - +
+

Generated by GdUnit4 at ${buid_date}

+
+ + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+
-
-
+ diff --git a/addons/gdUnit4/src/report/template/suite_report.html b/addons/gdUnit4/src/report/template/suite_report.html index 8418d2b2..bf55cf2f 100644 --- a/addons/gdUnit4/src/report/template/suite_report.html +++ b/addons/gdUnit4/src/report/template/suite_report.html @@ -2,10 +2,12 @@ + - Testsuite results - + + GdUnit4 Testsuite + @@ -24,90 +26,149 @@ - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
Tests${test_count}
Skipped${skipped_count}
Flaky${flaky_count}
Failures${failure_count}
Orphans${orphan_count}
Duration${duration}
-
-
-

Success Rate

- - - - -
${success_percent}
+
+ +
+

Testsuite Report

+
+ ${resource_path}
-
-

History

-

Coming Next

+
+
+ Tests + ${test_count} +
+
+ Skipped + ${skipped_count} +
+
+ Flaky + ${flaky_count} +
+
+ Failures + ${failure_count} +
+
+ Orphans + ${orphan_count} +
+
+ Duration + ${duration} +
+
+
+
+ Success Rate + ${success_percent} +
+
- +
-
-
-
- - - - - - - - - - - - ${report_table_tests} - -
TestcaseSkippedOrphansDurationReport
-
-
-

Failure Report

-
-
+
+
+
+ + + + + + + + + + + + + ${report_table_tests} + +
TestcaseResultSkippedOrphansDurationReport
+
+
+

Failure Report

+
-
+
-

Generated byGdUnit4 at ${buid_date}

+

Generated by GdUnit4 at ${buid_date}

+
+ + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+ + diff --git a/addons/gdUnit4/test/cmd/runtestDebug.tscn b/addons/gdUnit4/test/cmd/runtestDebug.tscn index fb78dace..5c600123 100644 --- a/addons/gdUnit4/test/cmd/runtestDebug.tscn +++ b/addons/gdUnit4/test/cmd/runtestDebug.tscn @@ -5,14 +5,23 @@ script/source = "# a small helper to debug the GdUnitCmdTool extends Node const CmdTool := preload(\"res://addons/gdUnit4/bin/GdUnitCmdTool.gd\") +const CopyLog := preload(\"res://addons/gdUnit4/bin/GdUnitCopyLog.gd\") # Called when the node enters the scene tree for the first time. func _ready() -> void: #var tool := CmdTool.new() var runner := CmdTool.CLIRunner.new() - runner._debug_cmd_args = [\"GdUnitCmdTool.gd\", \"--add\", \"res://addons/gdUnit4/test/GdUnitTestSuiteTest.gd\", \"--continue\"] + runner._debug_cmd_args = [\"GdUnitCmdTool.gd\", \"--add\", \"res://addons/gdUnit4/test/core/flaky_test.gd\", \"--continue\", \"-rc\", \"1\"] add_child(runner) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE: + var copy_log := CopyLog.new() + copy_log._debug_cmd_args = [\"GdUnitCopyLog.gd\"] + copy_log._process(0) + prints(\"exit\") " [node name="RuntestDebug" type="Node"] diff --git a/addons/gdUnit4/test/core/flaky_test.gd b/addons/gdUnit4/test/core/flaky_test.gd new file mode 100644 index 00000000..25ef20c0 --- /dev/null +++ b/addons/gdUnit4/test/core/flaky_test.gd @@ -0,0 +1,156 @@ +extends GdUnitTestSuite + + +var test_retries := { + "test_flaky_success" = 0, + "test_flaky_fail" = 0, + "test_success" = 0, + "test_paramaterized_flaky:0" = 0, + "test_paramaterized_flaky:1" = 0, + "test_paramaterized_flaky:2" = 0, + "test_paramaterized_flaky:3" = 0, + "test_paramaterized_flaky:4" = 0, + "test_paramaterized_flaky:5" = 0, + "test_fuzzed_flaky_success" = 0, + "test_fuzzed_flaky_fail" = 0 +} + +var _max_retries := 0 +var _flaky_check_enabled: bool +var _run_with_reries := 5 + + +class ValueSetFuzzer extends Fuzzer: + var _values := [0,1,2,3,4] + + func next_value() -> Variant: + return _values.pop_front() + + +@warning_ignore("unused_parameter") +func before(do_skip := true, skip_reason := "Do only activate for internal testing!") -> void: + _max_retries = GdUnitSettings.get_flaky_max_retries() + _flaky_check_enabled = GdUnitSettings.is_test_flaky_check_enabled() + ProjectSettings.set_setting(GdUnitSettings.TEST_FLAKY_CHECK, true) + ProjectSettings.set_setting(GdUnitSettings.TEST_FLAKY_MAX_RETRIES, _run_with_reries) + + +func after() -> void: + ProjectSettings.set_setting(GdUnitSettings.TEST_FLAKY_CHECK, _flaky_check_enabled) + ProjectSettings.set_setting(GdUnitSettings.TEST_FLAKY_MAX_RETRIES, _max_retries) + + var retry_count: int = test_retries["test_flaky_success"] + assert_int(retry_count)\ + .override_failure_message("Expecting 3 retries to succeed for test 'test_flaky_success'\n but was %d" % retry_count)\ + .is_equal(3) + retry_count = test_retries["test_flaky_fail"] + assert_int(retry_count)\ + .override_failure_message("Expecting %d retries for test 'test_flaky_fail'\n but was %d" % [_run_with_reries, retry_count])\ + .is_equal(_run_with_reries) + retry_count = test_retries["test_success"] + assert_int(retry_count)\ + .override_failure_message("Expecting one test iteration to succeed 'test_success'\n but was %d" % retry_count)\ + .is_equal(1) + # verify retry count of paramaterized test + retry_count = test_retries["test_paramaterized_flaky:0"] + assert_int(retry_count)\ + .override_failure_message("Expecting one test iteration to succeed 'test_paramaterized_flaky:0'\n but was %d" % retry_count)\ + .is_equal(1) + retry_count = test_retries["test_paramaterized_flaky:1"] + assert_int(retry_count)\ + .override_failure_message("Expecting one test iteration to succeed 'test_paramaterized_flaky:1'\n but was %d" % retry_count)\ + .is_equal(1) + retry_count = test_retries["test_paramaterized_flaky:2"] + assert_int(retry_count)\ + .override_failure_message("Expecting %d test iteration to fail 'test_paramaterized_flaky:2'\n but was %d" % [_run_with_reries, retry_count])\ + .is_equal(_run_with_reries) + retry_count = test_retries["test_paramaterized_flaky:3"] + assert_int(retry_count)\ + .override_failure_message("Expecting one test iteration to succeed 'test_paramaterized_flaky:3'\n but was %d" % retry_count)\ + .is_equal(1) + retry_count = test_retries["test_paramaterized_flaky:4"] + assert_int(retry_count)\ + .override_failure_message("Expecting 3 test iteration to succeed 'test_paramaterized_flaky:4'\n but was %d" % retry_count)\ + .is_equal(3) + # fuzzed tests + retry_count = test_retries["test_fuzzed_flaky_success"] + assert_int(retry_count)\ + .override_failure_message("Expecting 3 retries to succeed for test 'test_fuzzed_flaky_success'\n but was %d" % retry_count)\ + .is_equal(3) + retry_count = test_retries["test_fuzzed_flaky_fail"] + assert_int(retry_count)\ + .override_failure_message("Expecting %d retries for test 'test_fuzzed_flaky_fail'\n but was %d" % [_run_with_reries, retry_count])\ + .is_equal(_run_with_reries) + + +func test_flaky_success() -> void: + test_retries["test_flaky_success"] += 1 + var retry_count: int = test_retries["test_flaky_success"] + # do retry between 1 and 3 + assert_int(retry_count).is_less_equal(3) + if retry_count <= 2: + fail("failure 1: failed at retry %d" % retry_count) + fail("failure 2: failed at retry %d" % retry_count) + + +func test_flaky_fail() -> void: + test_retries["test_flaky_fail"] += 1 + var retry_count: int = test_retries["test_flaky_fail"] + # do retry between 1 and 5 + assert_int(retry_count).is_less_equal(6) + if retry_count < 6: + fail("failed on test retry %d" % retry_count) + + +func test_success() -> void: + test_retries["test_success"] += 1 + var retry_count: int = test_retries["test_success"] + # do retry only one time + assert_int(retry_count).is_equal(1) + assert_bool(true).is_true() + + +@warning_ignore("unused_parameter") +func test_paramaterized_flaky(test_index: int, expected_retry_count :int, test_parameters := [ + [0, 1], + [1, 1], + [2, 6], + [3, 1], + [4, 3]]) -> void: + + var test_case_name := "test_paramaterized_flaky:%d" % test_index + test_retries[test_case_name] += 1 + var retry_count: int = test_retries[test_case_name] + assert_int(retry_count).is_less_equal(expected_retry_count) + + if test_index == 2 or test_index == 4: + # do fail if retry_count less expected count to fail + if retry_count < expected_retry_count: + fail("failed at retry %d" % retry_count) + + +@warning_ignore("unused_parameter") +func test_fuzzed_flaky_success(fuzzer := ValueSetFuzzer.new(), fuzzer_iterations := 5) -> void: + var fuzzer_value: int = fuzzer.next_value() + if fuzzer_value == 0: + test_retries["test_fuzzed_flaky_success"] += 1 + var retry_count :int = test_retries["test_fuzzed_flaky_success"] + # do retry between 1 and 3 + assert_int(retry_count).is_less_equal(3) + + if retry_count <= 2: + fail("failure 1: failed at retry %d" % retry_count) + fail("failure 2: failed at retry %d" % retry_count) + + +@warning_ignore("unused_parameter") +func test_fuzzed_flaky_fail(fuzzer := ValueSetFuzzer.new(), fuzzer_iterations := 5) -> void: + var fuzzer_value: int = fuzzer.next_value() + if fuzzer_value == 0: + test_retries["test_fuzzed_flaky_fail"] += 1 + var retry_count :int = test_retries["test_fuzzed_flaky_fail"] + # do retry between 1 and 3 + assert_int(retry_count).is_less_equal(6) + + if retry_count < 6: + fail("failed at retry %d" % retry_count)