Vendor gdUnit4 for Godot 4 client testing (D-030). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
4.2 KiB
GDScript
115 lines
4.2 KiB
GDScript
## GdUnit4CSharpApiLoader
|
|
##
|
|
## A bridge class that handles communication between GDScript and C# for the GdUnit4 testing framework.
|
|
## This loader acts as a compatibility layer to safely access the .NET API and ensure that calls
|
|
## only proceed when the .NET environment is properly configured and available.
|
|
## [br]
|
|
## The class handles:
|
|
## - Verification of .NET runtime availability
|
|
## - Loading the C# wrapper script
|
|
## - Checking for the GdUnit4Api assembly
|
|
## - Providing proxy methods to access GdUnit4 functionality in C#
|
|
@static_unload
|
|
class_name GdUnit4CSharpApiLoader
|
|
extends RefCounted
|
|
|
|
## Cached reference to the loaded C# wrapper script
|
|
static var _gdUnit4NetWrapper: Script
|
|
|
|
## Cached instance of the API (singleton pattern)
|
|
static var _api_instance: RefCounted
|
|
|
|
|
|
class TestEventListener extends RefCounted:
|
|
|
|
func publish_event(event: Dictionary) -> void:
|
|
var test_event := GdUnitEvent.new().deserialize(event)
|
|
GdUnitSignals.instance().gdunit_event.emit(test_event)
|
|
|
|
static var _test_event_listener := TestEventListener.new()
|
|
|
|
|
|
## Returns an instance of the GdUnit4CSharpApi wrapper.[br]
|
|
## @return Script: The loaded C# wrapper or null if .NET is not supported
|
|
static func instance() -> Script:
|
|
if not GdUnit4CSharpApiLoader.is_api_loaded():
|
|
return null
|
|
|
|
return _gdUnit4NetWrapper
|
|
|
|
|
|
## Returns or creates a single instance of the API [br]
|
|
## This improves performance by reusing the same object
|
|
static func api_instance() -> RefCounted:
|
|
if _api_instance == null and is_api_loaded():
|
|
@warning_ignore("unsafe_method_access")
|
|
_api_instance = instance().new()
|
|
return _api_instance
|
|
|
|
|
|
static func is_engine_version_supported(engine_version: int = Engine.get_version_info().hex) -> bool:
|
|
return engine_version >= 0x40200
|
|
|
|
|
|
## Checks if the .NET environment is properly configured and available.[br]
|
|
## @return bool: True if .NET is fully supported and the assembly is found
|
|
static func is_api_loaded() -> bool:
|
|
# If the wrapper is already loaded we don't need to check again
|
|
if _gdUnit4NetWrapper != null:
|
|
return true
|
|
|
|
# First we check if this is a Godot .NET runtime instance
|
|
if not ClassDB.class_exists("CSharpScript") or not is_engine_version_supported():
|
|
return false
|
|
# Second we check the C# project file exists
|
|
var assembly_name: String = ProjectSettings.get_setting("dotnet/project/assembly_name")
|
|
if assembly_name.is_empty() or not FileAccess.file_exists("res://%s.csproj" % assembly_name):
|
|
return false
|
|
|
|
# Finally load the wrapper and check if the GdUnit4 assembly can be found
|
|
_gdUnit4NetWrapper = load("res://addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs")
|
|
@warning_ignore("unsafe_method_access")
|
|
return _gdUnit4NetWrapper.call("IsApiLoaded")
|
|
|
|
|
|
## Returns the version of the GdUnit4 .NET assembly.[br]
|
|
## @return String: The version string or "unknown" if .NET is not supported
|
|
static func version() -> String:
|
|
if not GdUnit4CSharpApiLoader.is_api_loaded():
|
|
return "unknown"
|
|
@warning_ignore("unsafe_method_access")
|
|
return instance().Version()
|
|
|
|
|
|
static func discover_tests(source_script: Script) -> Array[GdUnitTestCase]:
|
|
var tests: Array = _gdUnit4NetWrapper.call("DiscoverTests", source_script)
|
|
|
|
return Array(tests.map(GdUnitTestCase.from_dict), TYPE_OBJECT, "RefCounted", GdUnitTestCase)
|
|
|
|
|
|
static func execute(tests: Array[GdUnitTestCase]) -> void:
|
|
var net_api := api_instance()
|
|
if net_api == null:
|
|
push_warning("Execute C# tests not supported!")
|
|
return
|
|
var tests_as_dict: Array[Dictionary] = Array(tests.map(GdUnitTestCase.to_dict), TYPE_DICTIONARY, "", null)
|
|
|
|
net_api.call("ExecuteAsync", tests_as_dict, _test_event_listener.publish_event)
|
|
@warning_ignore("unsafe_property_access")
|
|
await net_api.ExecutionCompleted
|
|
|
|
|
|
static func create_test_suite(source_path: String, line_number: int, test_suite_path: String) -> GdUnitResult:
|
|
if not GdUnit4CSharpApiLoader.is_api_loaded():
|
|
return GdUnitResult.error("Can't create test suite. No .NET support found.")
|
|
@warning_ignore("unsafe_method_access")
|
|
var result: Dictionary = instance().CreateTestSuite(source_path, line_number, test_suite_path)
|
|
if result.has("error"):
|
|
return GdUnitResult.error(str(result.get("error")))
|
|
return GdUnitResult.success(result)
|
|
|
|
|
|
static func is_csharp_file(resource_path: String) -> bool:
|
|
var ext := resource_path.get_extension()
|
|
return ext == "cs" and GdUnit4CSharpApiLoader.is_api_loaded()
|