refactor: enhance test framework with automated resource tracking and scripted error capture capabilities

This commit is contained in:
2026-06-26 09:40:17 +08:00
parent 948a99cf90
commit 00f9d98f4b
58 changed files with 3594 additions and 1289 deletions
+53
View File
@@ -31,6 +31,34 @@ func suite_teardown() -> void:
pass
# ----- tracked allocations (freed by the runner after each test) -----
var _tracked_objects: Array[Object] = []
## Register a manually-managed Object (plain Object or out-of-tree Node) so
## the runner frees it after the current test. RefCounted instances are
## accepted but ignored because they manage their own lifetime.
func track(obj: Object) -> Object:
if obj != null and not obj is RefCounted:
_tracked_objects.append(obj)
return obj
## Free everything registered via track(). Called by the runner after each
## test's teardown() and again after suite_teardown().
func _free_tracked() -> void:
for obj in _tracked_objects:
if not is_instance_valid(obj) or (obj is Node and obj.is_queued_for_deletion()):
continue
if obj is Node:
var parent := (obj as Node).get_parent()
if parent != null:
parent.remove_child(obj)
obj.free()
_tracked_objects.clear()
# ----- assertion state (managed by McpTestRunner) -----
var _failed: bool = false
@@ -38,6 +66,7 @@ var _message: String = ""
var _assertion_count: int = 0
var _skipped: bool = false
var _skip_reason: String = ""
var _expected_script_error_substrings: Array[String] = []
# ----- suite-level state (managed by McpTestRunner) -----
@@ -53,6 +82,7 @@ func _reset() -> void:
_assertion_count = 0
_skipped = false
_skip_reason = ""
_expected_script_error_substrings.clear()
func _reset_suite_state() -> void:
@@ -121,6 +151,29 @@ func skip_on_godot_lt(min_version: String, reason: String = "") -> bool:
return false
## Allow one captured SCRIPT ERROR whose text contains `substring`.
## Use only for negative-path tests that intentionally compile or execute
## invalid GDScript and assert on the resulting diagnostics.
func expect_script_error_containing(substring: String) -> void:
_expected_script_error_substrings.append(substring)
func _unexpected_script_errors(captured: PackedStringArray) -> PackedStringArray:
var unexpected := PackedStringArray()
var remaining := _expected_script_error_substrings.duplicate()
for error in captured:
var matched_index := -1
for i in range(remaining.size()):
if error.find(remaining[i]) != -1:
matched_index = i
break
if matched_index == -1:
unexpected.append(error)
else:
remaining.remove_at(matched_index)
return unexpected
## Trigger an undo against whichever history (scene or global) holds the most
## recent action. `EditorUndoRedoManager` in Godot 4.x doesn't expose `.undo()`
## directly — you resolve the history's underlying UndoRedo and call it there.