setting up GDUnit
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 3m40s
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 3m40s
This commit is contained in:
269
addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd
Normal file
269
addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd
Normal file
@@ -0,0 +1,269 @@
|
||||
## The execution context
|
||||
## It contains all the necessary information about the executed stage, such as memory observers, reports, orphan monitor
|
||||
class_name GdUnitExecutionContext
|
||||
|
||||
enum GC_ORPHANS_CHECK {
|
||||
NONE,
|
||||
SUITE_HOOK_AFTER,
|
||||
TEST_HOOK_AFTER,
|
||||
TEST_CASE
|
||||
}
|
||||
|
||||
|
||||
var _parent_context: GdUnitExecutionContext
|
||||
var _sub_context: Array[GdUnitExecutionContext] = []
|
||||
var _orphan_monitor: GdUnitOrphanNodesMonitor
|
||||
var _memory_observer: GdUnitMemoryObserver
|
||||
var _report_collector: GdUnitTestReportCollector
|
||||
var _timer: LocalTime
|
||||
var _test_case_name: StringName
|
||||
var _test_case_parameter_set: Array
|
||||
var _name: String
|
||||
var _test_execution_iteration: int = 0
|
||||
var _flaky_test_check := GdUnitSettings.is_test_flaky_check_enabled()
|
||||
var _flaky_test_retries := GdUnitSettings.get_flaky_max_retries()
|
||||
var _orphans := -1
|
||||
|
||||
|
||||
var error_monitor: GodotGdErrorMonitor = null:
|
||||
get:
|
||||
if _parent_context != null:
|
||||
return _parent_context.error_monitor
|
||||
if error_monitor == null:
|
||||
error_monitor = GodotGdErrorMonitor.new()
|
||||
return error_monitor
|
||||
|
||||
|
||||
var test_suite: GdUnitTestSuite = null:
|
||||
get:
|
||||
if _parent_context != null:
|
||||
return _parent_context.test_suite
|
||||
return test_suite
|
||||
|
||||
|
||||
var test_case: _TestCase = null:
|
||||
get:
|
||||
if test_case == null and _parent_context != null:
|
||||
return _parent_context.test_case
|
||||
return test_case
|
||||
|
||||
|
||||
func _init(name: StringName, parent_context: GdUnitExecutionContext = null) -> void:
|
||||
_name = name
|
||||
_parent_context = parent_context
|
||||
_timer = LocalTime.now()
|
||||
_orphan_monitor = GdUnitOrphanNodesMonitor.new(name)
|
||||
_orphan_monitor.start()
|
||||
_memory_observer = GdUnitMemoryObserver.new()
|
||||
_report_collector = GdUnitTestReportCollector.new()
|
||||
if parent_context != null:
|
||||
parent_context._sub_context.append(self)
|
||||
|
||||
|
||||
func dispose() -> void:
|
||||
_timer = null
|
||||
_orphan_monitor = null
|
||||
_report_collector = null
|
||||
_memory_observer = null
|
||||
_parent_context = null
|
||||
test_suite = null
|
||||
test_case = null
|
||||
dispose_sub_contexts()
|
||||
|
||||
|
||||
func dispose_sub_contexts() -> void:
|
||||
for context in _sub_context:
|
||||
context.dispose()
|
||||
_sub_context.clear()
|
||||
|
||||
|
||||
static func of(pe: GdUnitExecutionContext) -> GdUnitExecutionContext:
|
||||
var context := GdUnitExecutionContext.new(pe._test_case_name, pe)
|
||||
context._test_case_name = pe._test_case_name
|
||||
context._test_execution_iteration = pe._test_execution_iteration
|
||||
return context
|
||||
|
||||
|
||||
static func of_test_suite(p_test_suite: GdUnitTestSuite) -> GdUnitExecutionContext:
|
||||
assert(p_test_suite, "test_suite is null")
|
||||
var context := GdUnitExecutionContext.new(p_test_suite.get_name())
|
||||
context.test_suite = p_test_suite
|
||||
return context
|
||||
|
||||
|
||||
static func of_test_case(pe: GdUnitExecutionContext, p_test_case: _TestCase) -> GdUnitExecutionContext:
|
||||
assert(p_test_case, "test_case is null")
|
||||
var context := GdUnitExecutionContext.new(p_test_case.get_name(), pe)
|
||||
context.test_case = p_test_case
|
||||
return context
|
||||
|
||||
|
||||
static func of_parameterized_test(pe: GdUnitExecutionContext, test_case_name: String, test_case_parameter_set: Array) -> GdUnitExecutionContext:
|
||||
var context := GdUnitExecutionContext.new(test_case_name, pe)
|
||||
context._test_case_name = test_case_name
|
||||
context._test_case_parameter_set = test_case_parameter_set
|
||||
return context
|
||||
|
||||
|
||||
func get_test_suite_path() -> String:
|
||||
return test_suite.get_script().resource_path
|
||||
|
||||
|
||||
func get_test_suite_name() -> StringName:
|
||||
return test_suite.get_name()
|
||||
|
||||
|
||||
func get_test_case_name() -> StringName:
|
||||
if _test_case_name.is_empty():
|
||||
return test_case._test_case.display_name
|
||||
return _test_case_name
|
||||
|
||||
|
||||
func error_monitor_start() -> void:
|
||||
error_monitor.start()
|
||||
|
||||
|
||||
func error_monitor_stop() -> void:
|
||||
await error_monitor.scan()
|
||||
for error_report in error_monitor.to_reports():
|
||||
if error_report.is_error():
|
||||
_report_collector.push_back(error_report)
|
||||
|
||||
|
||||
func orphan_monitor_start() -> void:
|
||||
_orphan_monitor.start()
|
||||
|
||||
|
||||
func orphan_monitor_stop() -> void:
|
||||
_orphan_monitor.stop()
|
||||
|
||||
|
||||
func add_report(report: GdUnitReport) -> GdUnitReport:
|
||||
_report_collector.push_back(report)
|
||||
return report
|
||||
|
||||
|
||||
func reports() -> Array[GdUnitReport]:
|
||||
return _report_collector.reports()
|
||||
|
||||
|
||||
func collect_reports(recursive: bool) -> Array[GdUnitReport]:
|
||||
if not recursive:
|
||||
return reports()
|
||||
|
||||
# we combine the reports of test_before(), test_after() and test() to be reported by `fire_test_ended`
|
||||
# we strictly need to copy the reports before adding sub context reports to avoid manipulation of the current context
|
||||
var current_reports := reports().duplicate()
|
||||
for sub_context in _sub_context:
|
||||
current_reports.append_array(sub_context.collect_reports(true))
|
||||
|
||||
return current_reports
|
||||
|
||||
|
||||
func calculate_statistics(reports_: Array[GdUnitReport]) -> Dictionary:
|
||||
var failed_count := GdUnitTestReportCollector.count_failures(reports_)
|
||||
var error_count := GdUnitTestReportCollector.count_errors(reports_)
|
||||
var warn_count := GdUnitTestReportCollector.count_warnings(reports_)
|
||||
var skip_count := GdUnitTestReportCollector.count_skipped(reports_)
|
||||
var is_failed := !is_success()
|
||||
var orphan_count := _count_orphans()
|
||||
var elapsed_time := _timer.elapsed_since_ms()
|
||||
var retries := 1 if _parent_context == null else _sub_context.size()
|
||||
# Mark as flaky if it is successful, but errors were counted
|
||||
var is_flaky := retries > 1 and not is_failed
|
||||
# In the case of a flakiness test, we do not report an error counter, as an unreliable test is considered successful
|
||||
# after a certain number of repetitions.
|
||||
if is_flaky:
|
||||
failed_count = 0
|
||||
|
||||
return {
|
||||
GdUnitEvent.RETRY_COUNT: retries,
|
||||
GdUnitEvent.ELAPSED_TIME: elapsed_time,
|
||||
GdUnitEvent.FAILED: is_failed,
|
||||
GdUnitEvent.ERRORS: error_count > 0,
|
||||
GdUnitEvent.WARNINGS: warn_count > 0,
|
||||
GdUnitEvent.FLAKY: is_flaky,
|
||||
GdUnitEvent.SKIPPED: skip_count > 0,
|
||||
GdUnitEvent.FAILED_COUNT: failed_count,
|
||||
GdUnitEvent.ERROR_COUNT: error_count,
|
||||
GdUnitEvent.SKIPPED_COUNT: skip_count,
|
||||
GdUnitEvent.ORPHAN_NODES: orphan_count,
|
||||
}
|
||||
|
||||
|
||||
func is_success() -> bool:
|
||||
if _sub_context.is_empty():
|
||||
return not _report_collector.has_failures()
|
||||
# we on test suite level?
|
||||
if _parent_context == null:
|
||||
return not _report_collector.has_failures()
|
||||
|
||||
return _sub_context[-1].is_success() and not _report_collector.has_failures()
|
||||
|
||||
|
||||
func is_skipped() -> bool:
|
||||
return (
|
||||
_sub_context.any(func(c :GdUnitExecutionContext) -> bool:
|
||||
return c.is_skipped())
|
||||
or test_case.is_skipped() if test_case != null else false
|
||||
)
|
||||
|
||||
|
||||
func is_interupted() -> bool:
|
||||
return false if test_case == null else test_case.is_interupted()
|
||||
|
||||
|
||||
func _count_orphans() -> int:
|
||||
if _orphans != -1:
|
||||
return _orphans
|
||||
|
||||
var orphans := 0
|
||||
for c in _sub_context:
|
||||
if _orphan_monitor.orphan_nodes() != c._orphan_monitor.orphan_nodes():
|
||||
orphans += c._count_orphans()
|
||||
|
||||
_orphans = _orphan_monitor.orphan_nodes()
|
||||
if _orphan_monitor.orphan_nodes() != orphans:
|
||||
_orphans -= orphans
|
||||
|
||||
return _orphans
|
||||
|
||||
|
||||
func sum(accum: int, number: int) -> int:
|
||||
return accum + number
|
||||
|
||||
|
||||
func retry_execution() -> bool:
|
||||
var retry := _test_execution_iteration < 1 if not _flaky_test_check else _test_execution_iteration < _flaky_test_retries
|
||||
if retry:
|
||||
_test_execution_iteration += 1
|
||||
return retry
|
||||
|
||||
|
||||
func register_auto_free(obj: Variant) -> Variant:
|
||||
return _memory_observer.register_auto_free(obj)
|
||||
|
||||
|
||||
## Runs the gdunit garbage collector to free registered object and handle orphan node reporting
|
||||
func gc(gc_orphan_check: GC_ORPHANS_CHECK = GC_ORPHANS_CHECK.NONE) -> void:
|
||||
# unreference last used assert form the test to prevent memory leaks
|
||||
GdUnitThreadManager.get_current_context().clear_assert()
|
||||
await _memory_observer.gc()
|
||||
orphan_monitor_stop()
|
||||
|
||||
var orphans := _count_orphans()
|
||||
match(gc_orphan_check):
|
||||
GC_ORPHANS_CHECK.SUITE_HOOK_AFTER:
|
||||
if orphans > 0:
|
||||
reports().push_front(GdUnitReport.new() \
|
||||
.create(GdUnitReport.WARN, 1, GdAssertMessages.orphan_detected_on_suite_setup(orphans)))
|
||||
|
||||
GC_ORPHANS_CHECK.TEST_HOOK_AFTER:
|
||||
if orphans > 0:
|
||||
reports().push_front(GdUnitReport.new()\
|
||||
.create(GdUnitReport.WARN, 1, GdAssertMessages.orphan_detected_on_test_setup(orphans)))
|
||||
|
||||
GC_ORPHANS_CHECK.TEST_CASE:
|
||||
if orphans > 0:
|
||||
reports().push_front(GdUnitReport.new()\
|
||||
.create(GdUnitReport.WARN, test_case.line_number(), GdAssertMessages.orphan_detected_on_test(orphans)))
|
||||
Reference in New Issue
Block a user