diff --git a/addons/gdUnit4/GdUnitRunner.cfg b/addons/gdUnit4/GdUnitRunner.cfg deleted file mode 100644 index faa4aac2..00000000 --- a/addons/gdUnit4/GdUnitRunner.cfg +++ /dev/null @@ -1,402 +0,0 @@ -{ - "server_port": 31002, - "tests": [ - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_single", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_single", - "guid": "f421405c-d35d474-994a918-1eaa448197", - "line_number": 4, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_single" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_polygon", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_polygon", - "guid": "ccf33aaf-32b0444-eb47d2e-7eff08b57d", - "line_number": 11, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_polygon" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_spread", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_spread", - "guid": "3fb41868-d98f450-ab0b6a7-5edf2f8da6", - "line_number": 18, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_spread" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_stack", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_stack", - "guid": "59cceb23-691d40b-2b581d5-afd2ab1680", - "line_number": 25, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_stack" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_shape_2d", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_shape_2d", - "guid": "b6690147-519044d-492d34b-d0ee130368", - "line_number": 32, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_shape_2d" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_custom_shape_2d", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_custom_shape_2d", - "guid": "c47ebc31-33c744b-1816afc-fa3f9a416a", - "line_number": 39, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_custom_shape_2d" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_loop", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_loop", - "guid": "3f879538-8ff5482-eaf1422-bb8e25b421", - "line_number": 46, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_loop" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_pattern_composer_group", - "fully_qualified_name": "tests.pattern_composer.test_gdunit_pattern_composer.test_pattern_composer_group", - "guid": "853d43aa-c06b46e-484c1d3-c414a6a18d", - "line_number": 53, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "suite_name": "test_gdunit_pattern_composer", - "suite_resource_path": "res://tests/pattern_composer/test_gdunit_pattern_composer.gd", - "test_name": "test_pattern_composer_group" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_projectile_template_simple", - "fully_qualified_name": "tests.projectile_template.test_gdunit_projectile_template.test_projectile_template_simple", - "guid": "3a8a0891-74894e3-0b3072f-24982e8325", - "line_number": 4, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "suite_name": "test_gdunit_projectile_template", - "suite_resource_path": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "test_name": "test_projectile_template_simple" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_projectile_template_advanced", - "fully_qualified_name": "tests.projectile_template.test_gdunit_projectile_template.test_projectile_template_advanced", - "guid": "1fa76976-dd3f4fe-986e1ef-9792777d7a", - "line_number": 10, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "suite_name": "test_gdunit_projectile_template", - "suite_resource_path": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "test_name": "test_projectile_template_advanced" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_projectile_template_custom", - "fully_qualified_name": "tests.projectile_template.test_gdunit_projectile_template.test_projectile_template_custom", - "guid": "25fc1a9e-3999446-49460d4-805d018b57", - "line_number": 16, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "suite_name": "test_gdunit_projectile_template", - "suite_resource_path": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "test_name": "test_projectile_template_custom" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_projectile_template_node_2d", - "fully_qualified_name": "tests.projectile_template.test_gdunit_projectile_template.test_projectile_template_node_2d", - "guid": "51a1f0e4-2ce9452-d949a02-142281424e", - "line_number": 23, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "suite_name": "test_gdunit_projectile_template", - "suite_resource_path": "res://tests/projectile_template/test_gdunit_projectile_template.gd", - "test_name": "test_projectile_template_node_2d" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_example_scene_1", - "fully_qualified_name": "tests.test_example_scenes.test_example_scenes.test_example_scene_1", - "guid": "dfbd9afc-130f488-fbe8066-873fcce908", - "line_number": 3, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/test_example_scenes/test_example_scenes.gd", - "suite_name": "test_example_scenes", - "suite_resource_path": "res://tests/test_example_scenes/test_example_scenes.gd", - "test_name": "test_example_scene_1" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_example_scene_2", - "fully_qualified_name": "tests.test_example_scenes.test_example_scenes.test_example_scene_2", - "guid": "c2197076-b306433-da5dcda-551adeaf2b", - "line_number": 9, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/test_example_scenes/test_example_scenes.gd", - "suite_name": "test_example_scenes", - "suite_resource_path": "res://tests/test_example_scenes/test_example_scenes.gd", - "test_name": "test_example_scene_2" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_example_scene_3", - "fully_qualified_name": "tests.test_example_scenes.test_example_scenes.test_example_scene_3", - "guid": "99a8034a-f6c2415-4a3e9f8-58a65de46a", - "line_number": 15, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/test_example_scenes/test_example_scenes.gd", - "suite_name": "test_example_scenes", - "suite_resource_path": "res://tests/test_example_scenes/test_example_scenes.gd", - "test_name": "test_example_scene_3" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_example_scene_4", - "fully_qualified_name": "tests.test_example_scenes.test_example_scenes.test_example_scene_4", - "guid": "9df021eb-9efb47c-38d9d74-0e691f008d", - "line_number": 21, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/test_example_scenes/test_example_scenes.gd", - "suite_name": "test_example_scenes", - "suite_resource_path": "res://tests/test_example_scenes/test_example_scenes.gd", - "test_name": "test_example_scene_4" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_example_scene_5", - "fully_qualified_name": "tests.test_example_scenes.test_example_scenes.test_example_scene_5", - "guid": "a1504d0c-f9ee408-2b78438-8f28b14aff", - "line_number": 27, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/test_example_scenes/test_example_scenes.gd", - "suite_name": "test_example_scenes", - "suite_resource_path": "res://tests/test_example_scenes/test_example_scenes.gd", - "test_name": "test_example_scene_5" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_example_scene_6", - "fully_qualified_name": "tests.test_example_scenes.test_example_scenes.test_example_scene_6", - "guid": "61561bf5-abe8447-7899f00-f72e5fc690", - "line_number": 33, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/test_example_scenes/test_example_scenes.gd", - "suite_name": "test_example_scenes", - "suite_resource_path": "res://tests/test_example_scenes/test_example_scenes.gd", - "test_name": "test_example_scene_6" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_projectile_template_simple", - "fully_qualified_name": "tests.test_projectile_wrapper.test_projectile_wrappers.test_projectile_template_simple", - "guid": "90356574-0ea645f-ea0295a-918a476b8d", - "line_number": 4, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/test_projectile_wrapper/test_projectile_wrappers.gd", - "suite_name": "test_projectile_wrappers", - "suite_resource_path": "res://tests/test_projectile_wrapper/test_projectile_wrappers.gd", - "test_name": "test_projectile_template_simple" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_timing_scheduler_cooldown", - "fully_qualified_name": "tests.timing_scheduler.test_gdunit_timing_scheduler.test_timing_scheduler_cooldown", - "guid": "6f9882f4-cb8145e-9a07fbf-bf6adcfb12", - "line_number": 4, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/timing_scheduler/test_gdunit_timing_scheduler.gd", - "suite_name": "test_gdunit_timing_scheduler", - "suite_resource_path": "res://tests/timing_scheduler/test_gdunit_timing_scheduler.gd", - "test_name": "test_timing_scheduler_cooldown" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_timing_scheduler_repeater", - "fully_qualified_name": "tests.timing_scheduler.test_gdunit_timing_scheduler.test_timing_scheduler_repeater", - "guid": "786cc6c5-385c49b-a9c5c7a-2e322018e2", - "line_number": 10, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/timing_scheduler/test_gdunit_timing_scheduler.gd", - "suite_name": "test_gdunit_timing_scheduler", - "suite_resource_path": "res://tests/timing_scheduler/test_gdunit_timing_scheduler.gd", - "test_name": "test_timing_scheduler_repeater" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_timing_scheduler_timing_set", - "fully_qualified_name": "tests.timing_scheduler.test_gdunit_timing_scheduler.test_timing_scheduler_timing_set", - "guid": "f231698c-e0084af-489a36c-888136a32c", - "line_number": 16, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://tests/timing_scheduler/test_gdunit_timing_scheduler.gd", - "suite_name": "test_gdunit_timing_scheduler", - "suite_resource_path": "res://tests/timing_scheduler/test_gdunit_timing_scheduler.gd", - "test_name": "test_timing_scheduler_timing_set" - } - ], - "version": "5.0" -} \ No newline at end of file diff --git a/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid b/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid index 2aec0f18..845ad42c 100644 --- a/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid +++ b/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid @@ -1 +1 @@ -uid://cvcdrd6j7raat +uid://b6ndm5kmgui1n diff --git a/addons/gdUnit4/bin/GdUnitCopyLog.gd b/addons/gdUnit4/bin/GdUnitCopyLog.gd index fdd63d2c..8b228051 100644 --- a/addons/gdUnit4/bin/GdUnitCopyLog.gd +++ b/addons/gdUnit4/bin/GdUnitCopyLog.gd @@ -82,9 +82,9 @@ func _process(_delta: float) -> bool: 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 + _report_root_path, GdUnitConstants.REPORT_DIR_PREFIX ) - _current_report_path = "%s/%s%d" % [_report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX, iteration] + _current_report_path = "%s/%s%d" % [_report_root_path, GdUnitConstants.REPORT_DIR_PREFIX, iteration] func set_report_directory(path: String) -> void: diff --git a/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid b/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid index 31c0fa94..cb4eb349 100644 --- a/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid +++ b/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid @@ -1 +1 @@ -uid://bfimf41feem4m +uid://byyjqylc7tk4j diff --git a/addons/gdUnit4/plugin.cfg b/addons/gdUnit4/plugin.cfg index 4b3cf178..6f734184 100644 --- a/addons/gdUnit4/plugin.cfg +++ b/addons/gdUnit4/plugin.cfg @@ -3,5 +3,5 @@ name="gdUnit4" description="Unit Testing Framework for Godot Scripts" author="Mike Schulze" -version="5.0.5" +version="6.0.0" script="plugin.gd" diff --git a/addons/gdUnit4/plugin.gd b/addons/gdUnit4/plugin.gd index 1a52ef3c..822c4d56 100644 --- a/addons/gdUnit4/plugin.gd +++ b/addons/gdUnit4/plugin.gd @@ -2,8 +2,8 @@ extends EditorPlugin # We need to define manually the slot id's, to be downwards compatible -const CONTEXT_SLOT_FILESYSTEM = 1 # EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM -const CONTEXT_SLOT_SCRIPT_EDITOR = 2 # EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR +const CONTEXT_SLOT_FILESYSTEM: int = 1 # EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM +const CONTEXT_SLOT_SCRIPT_EDITOR: int = 2 # EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR var _gd_inspector: Control var _gd_console: Control @@ -12,13 +12,22 @@ var _gd_scripteditor_context_menu: Variant func _enter_tree() -> void: + + var inferred_declaration: int = ProjectSettings.get_setting("debug/gdscript/warnings/inferred_declaration") + var exclude_addons: bool = ProjectSettings.get_setting("debug/gdscript/warnings/exclude_addons") + if !exclude_addons and inferred_declaration != 0: + printerr("GdUnit4: 'inferred_declaration' is set to Warning/Error!") + printerr("GdUnit4 is not 'inferred_declaration' save, you have to excluded addons (debug/gdscript/warnings/exclude_addons)") + printerr("Loading GdUnit4 Plugin failed.") + return + if check_running_in_test_env(): @warning_ignore("return_value_discarded") GdUnitCSIMessageWriter.new().prints_warning("It was recognized that GdUnit4 is running in a test environment, therefore the GdUnit4 plugin will not be executed!") return - if Engine.get_version_info().hex < 0x40300: - prints("The GdUnit4 plugin requires Godot version 4.3 or higher to run.") + if Engine.get_version_info().hex < 0x40500: + prints("This GdUnit4 plugin version '%s' requires Godot version '4.5' or higher to run." % GdUnit4Version.current()) return GdUnitSettings.setup() # Install the GdUnit Inspector @@ -27,7 +36,7 @@ func _enter_tree() -> void: add_control_to_dock(EditorPlugin.DOCK_SLOT_LEFT_UR, _gd_inspector) # Install the GdUnit Console _gd_console = (load("res://addons/gdUnit4/src/ui/GdUnitConsole.tscn") as PackedScene).instantiate() - var control := add_control_to_bottom_panel(_gd_console, "gdUnitConsole") + var control: Control = add_control_to_bottom_panel(_gd_console, "gdUnitConsole") @warning_ignore("unsafe_method_access") await _gd_console.setup_update_notification(control) if GdUnit4CSharpApiLoader.is_api_loaded(): @@ -51,14 +60,14 @@ func _exit_tree() -> void: if is_instance_valid(_gd_console): remove_control_from_bottom_panel(_gd_console) _gd_console.free() - var gdUnitTools := load("res://addons/gdUnit4/src/core/GdUnitTools.gd") + var gdUnitTools: GDScript = load("res://addons/gdUnit4/src/core/GdUnitTools.gd") @warning_ignore("unsafe_method_access") gdUnitTools.dispose_all(true) prints("Unload GdUnit4 Plugin success") func check_running_in_test_env() -> bool: - var args := OS.get_cmdline_args() + var args: PackedStringArray = OS.get_cmdline_args() args.append_array(OS.get_cmdline_user_args()) return DisplayServer.get_name() == "headless" or args.has("--selftest") or args.has("--add") or args.has("-a") or args.has("--quit-after") or args.has("--import") @@ -66,7 +75,7 @@ func check_running_in_test_env() -> bool: func _add_context_menus() -> void: if Engine.get_version_info().hex >= 0x40400: # With Godot 4.4 we have to use the 'add_context_menu_plugin' to register editor context menus - _gd_filesystem_context_menu = _create_context_menu("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx") + _gd_filesystem_context_menu = _preload_gdx_script("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx") call_deferred("add_context_menu_plugin", CONTEXT_SLOT_FILESYSTEM, _gd_filesystem_context_menu) # the CONTEXT_SLOT_SCRIPT_EDITOR is adding to the script panel instead of script editor see https://github.com/godotengine/godot/pull/100556 #_gd_scripteditor_context_menu = _preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx") @@ -86,13 +95,14 @@ func _remove_context_menus() -> void: call_deferred("remove_context_menu_plugin", _gd_scripteditor_context_menu) -func _create_context_menu(script_path: String) -> Variant: - var context_menu_script := GDScript.new() - context_menu_script.source_code = FileAccess.get_file_as_string(script_path) - var err := context_menu_script.reload(true) +func _preload_gdx_script(script_path: String) -> Variant: + var script: GDScript = GDScript.new() + script.source_code = GdUnitFileAccess.resource_as_string(script_path) + script.take_over_path(script_path) + var err :Error = script.reload() if err != OK: push_error("Can't create context menu %s, error: %s" % [script_path, error_string(err)]) - return context_menu_script.new() + return script.new() func _on_resource_saved(resource: Resource) -> void: diff --git a/addons/gdUnit4/plugin.gd.uid b/addons/gdUnit4/plugin.gd.uid index 413c7dd7..e6812203 100644 --- a/addons/gdUnit4/plugin.gd.uid +++ b/addons/gdUnit4/plugin.gd.uid @@ -1 +1 @@ -uid://b6jqtcf22jkil +uid://bofr1yjhccui diff --git a/addons/gdUnit4/runtest.cmd b/addons/gdUnit4/runtest.cmd index 3f4c9a82..ad5da4d9 100644 --- a/addons/gdUnit4/runtest.cmd +++ b/addons/gdUnit4/runtest.cmd @@ -2,7 +2,7 @@ setlocal enabledelayedexpansion :: Initialize variables -set "godot_bin=" +set "godot_binary=" set "filtered_args=" :: Process all arguments @@ -10,8 +10,8 @@ set "i=0" :parse_args if "%~1"=="" goto end_parse_args -if "%~1"=="--godot_bin" ( - set "godot_bin=%~2" +if "%~1"=="--godot_binary" ( + set "godot_binary=%~2" shift shift ) else ( @@ -21,28 +21,28 @@ if "%~1"=="--godot_bin" ( goto parse_args :end_parse_args -:: If --godot_bin wasn't provided, fallback to environment variable -if "!godot_bin!"=="" ( - set "godot_bin=%GODOT_BIN%" +:: If --godot_binary wasn't provided, fallback to environment variable +if "!godot_binary!"=="" ( + set "godot_binary=%GODOT_BIN%" ) -:: Check if we have a godot_bin value from any source -if "!godot_bin!"=="" ( +:: Check if we have a godot_binary value from any source +if "!godot_binary!"=="" ( echo Godot binary path is not specified. echo Please either: echo - Set the environment variable: set GODOT_BIN=C:\path\to\godot.exe - echo - Or use the --godot_bin argument: --godot_bin C:\path\to\godot.exe + echo - Or use the --godot_binary argument: --godot_binary C:\path\to\godot.exe exit /b 1 ) :: Check if the Godot binary exists -if not exist "!godot_bin!" ( - echo Error: The specified Godot binary '!godot_bin!' does not exist. +if not exist "!godot_binary!" ( + echo Error: The specified Godot binary '!godot_binary!' does not exist. exit /b 1 ) :: Get Godot version and check if it's a mono build -for /f "tokens=*" %%i in ('"!godot_bin!" --version') do set GODOT_VERSION=%%i +for /f "tokens=*" %%i in ('"!godot_binary!" --version') do set GODOT_VERSION=%%i echo !GODOT_VERSION! | findstr /I "mono" >nul if !errorlevel! equ 0 ( echo Godot .NET detected @@ -52,11 +52,11 @@ if !errorlevel! equ 0 ( ) :: Run the tests with the filtered arguments -"!godot_bin!" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd !filtered_args! +"!godot_binary!" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd !filtered_args! set exit_code=%ERRORLEVEL% echo Run tests ends with %exit_code% :: Run the copy log command -"!godot_bin!" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd !filtered_args! > nul +"!godot_binary!" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd !filtered_args! > nul set exit_code2=%ERRORLEVEL% exit /b %exit_code% diff --git a/addons/gdUnit4/runtest.sh b/addons/gdUnit4/runtest.sh index 9e3e006f..f0269efb 100644 --- a/addons/gdUnit4/runtest.sh +++ b/addons/gdUnit4/runtest.sh @@ -1,49 +1,49 @@ #!/bin/bash # Check for command-line argument -godot_bin="" +godot_binary="" filtered_args="" # Process all arguments with a more compatible approach while [ $# -gt 0 ]; do - if [ "$1" = "--godot_bin" ] && [ $# -gt 1 ]; then + if [ "$1" = "--godot_binary" ] && [ $# -gt 1 ]; then # Get the next argument as the value - godot_bin="$2" + godot_binary="$2" shift 2 else - # Keep non-godot_bin arguments for passing to Godot + # Keep non-godot_binary arguments for passing to Godot filtered_args="$filtered_args $1" shift fi done -# If --godot_bin wasn't provided, fallback to environment variable -if [ -z "$godot_bin" ]; then - godot_bin="$GODOT_BIN" +# If --godot_binary wasn't provided, fallback to environment variable +if [ -z "$godot_binary" ]; then + godot_binary="$GODOT_BIN" fi -# Check if we have a godot_bin value from any source -if [ -z "$godot_bin" ]; then +# Check if we have a godot_binary value from any source +if [ -z "$godot_binary" ]; then echo "Godot binary path is not specified." echo "Please either:" echo " - Set the environment variable: export GODOT_BIN=/path/to/godot" - echo " - Or use the --godot_bin argument: --godot_bin /path/to/godot" + echo " - Or use the --godot_binary argument: --godot_binary /path/to/godot" exit 1 fi # Check if the Godot binary exists and is executable -if [ ! -f "$godot_bin" ]; then - echo "Error: The specified Godot binary '$godot_bin' does not exist." +if [ ! -f "$godot_binary" ]; then + echo "Error: The specified Godot binary '$godot_binary' does not exist." exit 1 fi -if [ ! -x "$godot_bin" ]; then - echo "Error: The specified Godot binary '$godot_bin' is not executable." +if [ ! -x "$godot_binary" ]; then + echo "Error: The specified Godot binary '$godot_binary' is not executable." exit 1 fi # Get Godot version and check if it's a .NET build -GODOT_VERSION=$("$godot_bin" --version) +GODOT_VERSION=$("$godot_binary" --version) if echo "$GODOT_VERSION" | grep -i "mono" > /dev/null; then echo "Godot .NET detected" echo "Compiling c# classes ... Please Wait" @@ -52,11 +52,11 @@ if echo "$GODOT_VERSION" | grep -i "mono" > /dev/null; then fi # Run the tests with the filtered arguments -"$godot_bin" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $filtered_args +"$godot_binary" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $filtered_args exit_code=$? echo "Run tests ends with $exit_code" # Run the copy log command -"$godot_bin" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd $filtered_args > /dev/null +"$godot_binary" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd $filtered_args > /dev/null exit_code2=$? exit $exit_code diff --git a/addons/gdUnit4/src/Comparator.gd.uid b/addons/gdUnit4/src/Comparator.gd.uid index eebb181e..3af3c6d4 100644 --- a/addons/gdUnit4/src/Comparator.gd.uid +++ b/addons/gdUnit4/src/Comparator.gd.uid @@ -1 +1 @@ -uid://crwb16mgg0hhi +uid://ck7j1jr7s6y46 diff --git a/addons/gdUnit4/src/Fuzzers.gd.uid b/addons/gdUnit4/src/Fuzzers.gd.uid index 252d9e78..5d63f94f 100644 --- a/addons/gdUnit4/src/Fuzzers.gd.uid +++ b/addons/gdUnit4/src/Fuzzers.gd.uid @@ -1 +1 @@ -uid://bt0u6wsl4ad1x +uid://dxq47rabgbsc1 diff --git a/addons/gdUnit4/src/GdUnitArrayAssert.gd b/addons/gdUnit4/src/GdUnitArrayAssert.gd index cc2226dd..eeb7ca6b 100644 --- a/addons/gdUnit4/src/GdUnitArrayAssert.gd +++ b/addons/gdUnit4/src/GdUnitArrayAssert.gd @@ -1,111 +1,90 @@ ## An Assertion Tool to verify array values -class_name GdUnitArrayAssert +@abstract class_name GdUnitArrayAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitArrayAssert: - return self +@abstract func is_null() -> GdUnitArrayAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitArrayAssert: - return self +@abstract func is_not_null() -> GdUnitArrayAssert ## Verifies that the current Array is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_equal(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array is equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_equal_ignoring_case(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_not_equal(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array is not equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_not_equal_ignoring_case(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_not_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitArrayAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitArrayAssert ## Verifies that the current Array is empty, it has a size of 0. -func is_empty() -> GdUnitArrayAssert: - return self +@abstract func is_empty() -> GdUnitArrayAssert ## Verifies that the current Array is not empty, it has a size of minimum 1. -func is_not_empty() -> GdUnitArrayAssert: - return self +@abstract func is_not_empty() -> GdUnitArrayAssert + ## Verifies that the current Array is the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_same(expected: Variant) -> GdUnitArrayAssert ## Verifies that the current Array is NOT the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_not_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_not_same(expected: Variant) -> GdUnitArrayAssert ## Verifies that the current Array has a size of given value. -@warning_ignore("unused_parameter") -func has_size(expectd: int) -> GdUnitArrayAssert: - return self +@abstract func has_size(expectd: int) -> GdUnitArrayAssert ## Verifies that the current Array contains the given values, in any order.[br] ## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same] -@warning_ignore("unused_parameter") -func contains(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br] ## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly] -@warning_ignore("unused_parameter") -func contains_exactly(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_exactly(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br] ## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly_in_any_order] -@warning_ignore("unused_parameter") -func contains_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains the given values, in any order.[br] ## The values are compared by object reference, for deep parameter comparision use [method contains] -@warning_ignore("unused_parameter") -func contains_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_same(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br] ## The values are compared by object reference, for deep parameter comparision use [method contains_exactly] -@warning_ignore("unused_parameter") -func contains_same_exactly(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_same_exactly(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br] ## The values are compared by object reference, for deep parameter comparision use [method contains_exactly_in_any_order] -@warning_ignore("unused_parameter") -func contains_same_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_same_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array do NOT contains the given values, in any order.[br] @@ -113,13 +92,11 @@ func contains_same_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert: ## [b]Example:[/b] ## [codeblock] ## # will succeed -## assert_array([1, 2, 3, 4, 5]).not_contains([6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(6) ## # will fail -## assert_array([1, 2, 3, 4, 5]).not_contains([2, 6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(2, 6) ## [/codeblock] -@warning_ignore("unused_parameter") -func not_contains(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func not_contains(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array do NOT contains the given values, in any order.[br] @@ -127,45 +104,19 @@ func not_contains(expected :Variant) -> GdUnitArrayAssert: ## [b]Example:[/b] ## [codeblock] ## # will succeed -## assert_array([1, 2, 3, 4, 5]).not_contains([6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(6) ## # will fail -## assert_array([1, 2, 3, 4, 5]).not_contains([2, 6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(2, 6) ## [/codeblock] -@warning_ignore("unused_parameter") -func not_contains_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func not_contains_same(...expected: Array) -> GdUnitArrayAssert ## Extracts all values by given function name and optional arguments into a new ArrayAssert. ## If the elements not accessible by `func_name` the value is converted to `"n.a"`, expecting null values -@warning_ignore("unused_parameter") -func extract(func_name: String, args := Array()) -> GdUnitArrayAssert: - return self +@abstract func extract(func_name: String, ...func_args: Array) -> GdUnitArrayAssert ## Extracts all values by given extractor's into a new ArrayAssert. ## If the elements not extractable than the value is converted to `"n.a"`, expecting null values -@warning_ignore("unused_parameter") -func extractv( - extractor0 :GdUnitValueExtractor, - extractor1 :GdUnitValueExtractor = null, - extractor2 :GdUnitValueExtractor = null, - extractor3 :GdUnitValueExtractor = null, - extractor4 :GdUnitValueExtractor = null, - extractor5 :GdUnitValueExtractor = null, - extractor6 :GdUnitValueExtractor = null, - extractor7 :GdUnitValueExtractor = null, - extractor8 :GdUnitValueExtractor = null, - extractor9 :GdUnitValueExtractor = null) -> GdUnitArrayAssert: - return self - - - -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitArrayAssert: - return self - - -@warning_ignore("unused_parameter") -func append_failure_message(message :String) -> GdUnitArrayAssert: - return self +## -- The argument type is Array[GdUnitValueExtractor] +@abstract func extractv(...extractors: Array) -> GdUnitArrayAssert diff --git a/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid b/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid index ed1e2eef..05f0155e 100644 --- a/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid @@ -1 +1 @@ -uid://dgb5ah6ldldwa +uid://dyquognfew6u diff --git a/addons/gdUnit4/src/GdUnitAssert.gd b/addons/gdUnit4/src/GdUnitAssert.gd index 6b354750..41382d9f 100644 --- a/addons/gdUnit4/src/GdUnitAssert.gd +++ b/addons/gdUnit4/src/GdUnitAssert.gd @@ -1,49 +1,47 @@ ## Base interface of all GdUnit asserts -class_name GdUnitAssert +@abstract class_name GdUnitAssert extends RefCounted ## Verifies that the current value is null. -@warning_ignore("untyped_declaration") -func is_null(): - return self +@abstract func is_null() -> GdUnitAssert ## Verifies that the current value is not null. -@warning_ignore("untyped_declaration") -func is_not_null(): - return self +@abstract func is_not_null() -> GdUnitAssert ## Verifies that the current value is equal to expected one. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func is_equal(expected: Variant): - return self +@abstract func is_equal(expected: Variant) -> GdUnitAssert ## Verifies that the current value is not equal to expected one. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func is_not_equal(expected: Variant): - return self - - -@warning_ignore("untyped_declaration") -func do_fail(): - return self - - -## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func override_failure_message(message :String): - return self - - -## Appends a custom message to the failure message. -## This can be used to add additional infromations to the generated failure message. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func append_failure_message(message :String): - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitAssert + + +## Overrides the default failure message by given custom message.[br] +## This function allows you to replace the automatically generated failure message with a more specific +## or user-friendly message that better describes the test failure context.[br] +## Usage: +## [codeblock] +## # Override with custom context-specific message +## func test_player_inventory(): +## assert_that(player.get_item_count("sword"))\ +## .override_failure_message("Player should have exactly one sword")\ +## .is_equal(1) +## [/codeblock] +@abstract func override_failure_message(message: String) -> GdUnitAssert + + +## Appends a custom message to the failure message.[br] +## This can be used to add additional information to the generated failure message +## while keeping the original assertion details for better debugging context.[br] +## Usage: +## [codeblock] +## # Add context to existing failure message +## func test_player_health(): +## assert_that(player.health)\ +## .append_failure_message("Player was damaged by: %s" % last_damage_source)\ +## .is_greater(0) +## [/codeblock] +@abstract func append_failure_message(message: String) -> GdUnitAssert diff --git a/addons/gdUnit4/src/GdUnitAssert.gd.uid b/addons/gdUnit4/src/GdUnitAssert.gd.uid index 53dc1fa1..1d644646 100644 --- a/addons/gdUnit4/src/GdUnitAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitAssert.gd.uid @@ -1 +1 @@ -uid://dtytl5a3ye7vi +uid://codpyqfvfv8b5 diff --git a/addons/gdUnit4/src/GdUnitAwaiter.gd.uid b/addons/gdUnit4/src/GdUnitAwaiter.gd.uid index 43a15fe6..719614b0 100644 --- a/addons/gdUnit4/src/GdUnitAwaiter.gd.uid +++ b/addons/gdUnit4/src/GdUnitAwaiter.gd.uid @@ -1 +1 @@ -uid://7taa8m1m2bwk +uid://btmksd2jaq78v diff --git a/addons/gdUnit4/src/GdUnitBoolAssert.gd b/addons/gdUnit4/src/GdUnitBoolAssert.gd index de67a941..714f8fc5 100644 --- a/addons/gdUnit4/src/GdUnitBoolAssert.gd +++ b/addons/gdUnit4/src/GdUnitBoolAssert.gd @@ -1,41 +1,35 @@ ## An Assertion Tool to verify boolean values -class_name GdUnitBoolAssert +@abstract class_name GdUnitBoolAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitBoolAssert: - return self +@abstract func is_null() -> GdUnitBoolAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitBoolAssert: - return self +@abstract func is_not_null() -> GdUnitBoolAssert ## Verifies that the current value is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitBoolAssert: - return self +@abstract func is_equal(expected: Variant) -> GdUnitBoolAssert ## Verifies that the current value is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitBoolAssert: - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitBoolAssert -## Verifies that the current value is true. -func is_true() -> GdUnitBoolAssert: - return self +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitBoolAssert -## Verifies that the current value is false. -func is_false() -> GdUnitBoolAssert: - return self +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitBoolAssert -## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitBoolAssert: - return self +## Verifies that the current value is true. +@abstract func is_true() -> GdUnitBoolAssert + + +## Verifies that the current value is false. +@abstract func is_false() -> GdUnitBoolAssert diff --git a/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid b/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid index 6b009550..41cc5fe4 100644 --- a/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid @@ -1 +1 @@ -uid://dq66qtuuopoor +uid://2fj5wcksl3pu diff --git a/addons/gdUnit4/src/GdUnitConstants.gd b/addons/gdUnit4/src/GdUnitConstants.gd index 04458947..e43c75ab 100644 --- a/addons/gdUnit4/src/GdUnitConstants.gd +++ b/addons/gdUnit4/src/GdUnitConstants.gd @@ -4,3 +4,7 @@ extends RefCounted const NO_ARG :Variant = "<--null-->" const EXPECT_ASSERT_REPORT_FAILURES := "expect_assert_report_failures" + +## The maximum number of report history files to store +const DEFAULT_REPORT_HISTORY_COUNT = 20 +const REPORT_DIR_PREFIX = "report_" diff --git a/addons/gdUnit4/src/GdUnitConstants.gd.uid b/addons/gdUnit4/src/GdUnitConstants.gd.uid index d3f389a8..dbe07457 100644 --- a/addons/gdUnit4/src/GdUnitConstants.gd.uid +++ b/addons/gdUnit4/src/GdUnitConstants.gd.uid @@ -1 +1 @@ -uid://bnouixfv6guj4 +uid://dy30mj87113t diff --git a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd index 49dd3053..45cc62a4 100644 --- a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd +++ b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd @@ -1,105 +1,79 @@ ## An Assertion Tool to verify dictionary -class_name GdUnitDictionaryAssert +@abstract class_name GdUnitDictionaryAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitDictionaryAssert: - return self +@abstract func is_null() -> GdUnitDictionaryAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitDictionaryAssert: - return self +@abstract func is_not_null() -> GdUnitDictionaryAssert ## Verifies that the current dictionary is equal to the given one, ignoring order. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_equal(expected: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary is not equal to the given one, ignoring order. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitDictionaryAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitDictionaryAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitDictionaryAssert ## Verifies that the current dictionary is empty, it has a size of 0. -func is_empty() -> GdUnitDictionaryAssert: - return self +@abstract func is_empty() -> GdUnitDictionaryAssert ## Verifies that the current dictionary is not empty, it has a size of minimum 1. -func is_not_empty() -> GdUnitDictionaryAssert: - return self +@abstract func is_not_empty() -> GdUnitDictionaryAssert ## Verifies that the current dictionary is the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_same(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_same(expected: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary is NOT the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter") -func is_not_same(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_not_same(expected: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary has a size of given value. -@warning_ignore("unused_parameter") -func has_size(expected: int) -> GdUnitDictionaryAssert: - return self +@abstract func has_size(expected: int) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key(s).[br] ## The keys are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_keys] -@warning_ignore("unused_parameter") -func contains_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func contains_keys(...expected: Array) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key and value.[br] ## The key and value are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_key_value] -@warning_ignore("unused_parameter") -func contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert: - return self - - -## Verifies that the current dictionary not contains the given key(s).[br] -## This function is [b]deprecated[/b] you have to use [method not_contains_keys] instead -@warning_ignore("unused_parameter") -func contains_not_keys(expected :Array) -> GdUnitDictionaryAssert: - push_warning("Deprecated: 'contains_not_keys' is deprectated and will be removed soon, use `not_contains_keys` instead!") - return not_contains_keys(expected) +@abstract func contains_key_value(key: Variant, value: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary not contains the given key(s).[br] ## The keys are compared by deep parameter comparision, for object reference compare you have to use [method not_contains_same_keys] -@warning_ignore("unused_parameter") -func not_contains_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func not_contains_keys(...expected: Array) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key(s).[br] ## The keys are compared by object reference, for deep parameter comparision use [method contains_keys] -@warning_ignore("unused_parameter") -func contains_same_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func contains_same_keys(expected: Array) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key and value.[br] ## The key and value are compared by object reference, for deep parameter comparision use [method contains_key_value] -@warning_ignore("unused_parameter") -func contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func contains_same_key_value(key: Variant, value: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary not contains the given key(s). ## The keys are compared by object reference, for deep parameter comparision use [method not_contains_keys] -@warning_ignore("unused_parameter") -func not_contains_same_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func not_contains_same_keys(...expected: Array) -> GdUnitDictionaryAssert diff --git a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid index 2e003463..d94c993b 100644 --- a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid @@ -1 +1 @@ -uid://deub0cmwmiigq +uid://d00ro1175p5kq diff --git a/addons/gdUnit4/src/GdUnitFailureAssert.gd b/addons/gdUnit4/src/GdUnitFailureAssert.gd index 1ee987ff..6fec1910 100644 --- a/addons/gdUnit4/src/GdUnitFailureAssert.gd +++ b/addons/gdUnit4/src/GdUnitFailureAssert.gd @@ -1,37 +1,52 @@ ## An assertion tool to verify GDUnit asserts. ## This assert is for internal use only, to verify that failed asserts work as expected. -class_name GdUnitFailureAssert +@abstract class_name GdUnitFailureAssert extends GdUnitAssert +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitFailureAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitFailureAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitFailureAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFailureAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFailureAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFailureAssert + + ## Verifies if the executed assert was successful -func is_success() -> GdUnitFailureAssert: - return self +@abstract func is_success() -> GdUnitFailureAssert + ## Verifies if the executed assert has failed -func is_failed() -> GdUnitFailureAssert: - return self +@abstract func is_failed() -> GdUnitFailureAssert ## Verifies the failure line is equal to expected one. -@warning_ignore("unused_parameter") -func has_line(expected :int) -> GdUnitFailureAssert: - return self +@abstract func has_line(expected: int) -> GdUnitFailureAssert ## Verifies the failure message is equal to expected one. -@warning_ignore("unused_parameter") -func has_message(expected: String) -> GdUnitFailureAssert: - return self +@abstract func has_message(expected: String) -> GdUnitFailureAssert ## Verifies that the failure message starts with the expected message. -@warning_ignore("unused_parameter") -func starts_with_message(expected: String) -> GdUnitFailureAssert: - return self +@abstract func starts_with_message(expected: String) -> GdUnitFailureAssert ## Verifies that the failure message contains the expected message. -@warning_ignore("unused_parameter") -func contains_message(expected: String) -> GdUnitFailureAssert: - return self +@abstract func contains_message(expected: String) -> GdUnitFailureAssert diff --git a/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid b/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid index 77b7ca77..ef3444ee 100644 --- a/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid @@ -1 +1 @@ -uid://bwkcw37pf1hwn +uid://bkhpivghcswwj diff --git a/addons/gdUnit4/src/GdUnitFileAssert.gd b/addons/gdUnit4/src/GdUnitFileAssert.gd index 21bf21a6..771da906 100644 --- a/addons/gdUnit4/src/GdUnitFileAssert.gd +++ b/addons/gdUnit4/src/GdUnitFileAssert.gd @@ -1,19 +1,38 @@ -class_name GdUnitFileAssert +@abstract class_name GdUnitFileAssert extends GdUnitAssert -func is_file() -> GdUnitFileAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitFileAssert -func exists() -> GdUnitFileAssert: - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitFileAssert -func is_script() -> GdUnitFileAssert: - return self +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitFileAssert -@warning_ignore("unused_parameter") -func contains_exactly(expected_rows :Array) -> GdUnitFileAssert: - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFileAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFileAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFileAssert + + +@abstract func is_file() -> GdUnitFileAssert + + +@abstract func exists() -> GdUnitFileAssert + + +@abstract func is_script() -> GdUnitFileAssert + + +@abstract func contains_exactly(expected_rows :Array) -> GdUnitFileAssert diff --git a/addons/gdUnit4/src/GdUnitFileAssert.gd.uid b/addons/gdUnit4/src/GdUnitFileAssert.gd.uid index cf3ae876..b59b3fe0 100644 --- a/addons/gdUnit4/src/GdUnitFileAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFileAssert.gd.uid @@ -1 +1 @@ -uid://dslieapkedhan +uid://d1n3j64orkr3v diff --git a/addons/gdUnit4/src/GdUnitFloatAssert.gd b/addons/gdUnit4/src/GdUnitFloatAssert.gd index 6ce5f1e8..2695ab0e 100644 --- a/addons/gdUnit4/src/GdUnitFloatAssert.gd +++ b/addons/gdUnit4/src/GdUnitFloatAssert.gd @@ -1,83 +1,75 @@ ## An Assertion Tool to verify float values -class_name GdUnitFloatAssert +@abstract class_name GdUnitFloatAssert extends GdUnitAssert -## Verifies that the current String is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitFloatAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitFloatAssert -## Verifies that the current String is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitFloatAssert: - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitFloatAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitFloatAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFloatAssert ## Verifies that the current and expected value are approximately equal. -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_equal_approx(expected :float, approx :float) -> GdUnitFloatAssert: - return self +@abstract func is_equal_approx(expected: float, approx: float) -> GdUnitFloatAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFloatAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFloatAssert ## Verifies that the current value is less than the given one. -@warning_ignore("unused_parameter") -func is_less(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_less(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is less than or equal the given one. -@warning_ignore("unused_parameter") -func is_less_equal(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_less_equal(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is greater than the given one. -@warning_ignore("unused_parameter") -func is_greater(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_greater(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is greater than or equal the given one. -@warning_ignore("unused_parameter") -func is_greater_equal(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_greater_equal(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is negative. -func is_negative() -> GdUnitFloatAssert: - return self +@abstract func is_negative() -> GdUnitFloatAssert ## Verifies that the current value is not negative. -func is_not_negative() -> GdUnitFloatAssert: - return self +@abstract func is_not_negative() -> GdUnitFloatAssert ## Verifies that the current value is equal to zero. -func is_zero() -> GdUnitFloatAssert: - return self +@abstract func is_zero() -> GdUnitFloatAssert ## Verifies that the current value is not equal to zero. -func is_not_zero() -> GdUnitFloatAssert: - return self +@abstract func is_not_zero() -> GdUnitFloatAssert ## Verifies that the current value is in the given set of values. -@warning_ignore("unused_parameter") -func is_in(expected :Array) -> GdUnitFloatAssert: - return self +@abstract func is_in(expected: Array) -> GdUnitFloatAssert ## Verifies that the current value is not in the given set of values. -@warning_ignore("unused_parameter") -func is_not_in(expected :Array) -> GdUnitFloatAssert: - return self +@abstract func is_not_in(expected: Array) -> GdUnitFloatAssert ## Verifies that the current value is between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_between(from :float, to :float) -> GdUnitFloatAssert: - return self +@abstract func is_between(from: float, to: float) -> GdUnitFloatAssert diff --git a/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid b/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid index 0dfb39e0..c9c479c1 100644 --- a/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid @@ -1 +1 @@ -uid://b03ov1r54ef8r +uid://db1ai8givmai4 diff --git a/addons/gdUnit4/src/GdUnitFuncAssert.gd b/addons/gdUnit4/src/GdUnitFuncAssert.gd index 75e8ebd6..e8a49c51 100644 --- a/addons/gdUnit4/src/GdUnitFuncAssert.gd +++ b/addons/gdUnit4/src/GdUnitFuncAssert.gd @@ -1,56 +1,42 @@ ## An Assertion Tool to verify function callback values -class_name GdUnitFuncAssert +@abstract class_name GdUnitFuncAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_null() -> GdUnitFuncAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_not_null() -> GdUnitFuncAssert ## Verifies that the current value is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_equal(expected: Variant) -> GdUnitFuncAssert -## Verifies that the current value is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFuncAssert -## Verifies that the current value is true. -func is_true() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFuncAssert -## Verifies that the current value is false. -func is_false() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFuncAssert -## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitFuncAssert: - return self +## Verifies that the current value is true. +@abstract func is_true() -> GdUnitFuncAssert + + +## Verifies that the current value is false. +@abstract func is_false() -> GdUnitFuncAssert ## Sets the timeout in ms to wait the function returnd the expected value, if the time over a failure is emitted.[br] ## e.g.[br] ## do wait until 5s the function `is_state` is returns 10 [br] ## [code]assert_func(instance, "is_state").wait_until(5000).is_equal(10)[/code] -@warning_ignore("unused_parameter") -func wait_until(timeout :int) -> GdUnitFuncAssert: - return self +@abstract func wait_until(timeout: int) -> GdUnitFuncAssert diff --git a/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid b/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid index 0aa3efd2..acc83025 100644 --- a/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid @@ -1 +1 @@ -uid://b320gtesyssr4 +uid://cu3gibdhgxlbc diff --git a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd index c5e9f863..01711f9a 100644 --- a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd +++ b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd @@ -1,16 +1,38 @@ ## An assertion tool to verify for Godot runtime errors like assert() and push notifications like push_error(). -class_name GdUnitGodotErrorAssert +@abstract class_name GdUnitGodotErrorAssert extends GdUnitAssert +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitGodotErrorAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitGodotErrorAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitGodotErrorAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitGodotErrorAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitGodotErrorAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitGodotErrorAssert + + ## Verifies if the executed code runs without any runtime errors ## Usage: ## [codeblock] ## await assert_error().is_success() ## [/codeblock] -func is_success() -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_success() -> GdUnitGodotErrorAssert ## Verifies if the executed code runs into a runtime error @@ -18,10 +40,7 @@ func is_success() -> GdUnitGodotErrorAssert: ## [codeblock] ## await assert_error().is_runtime_error() ## [/codeblock] -@warning_ignore("unused_parameter") -func is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert ## Verifies if the executed code has a push_warning() used @@ -29,10 +48,7 @@ func is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert: ## [codeblock] ## await assert_error().is_push_warning() ## [/codeblock] -@warning_ignore("unused_parameter") -func is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert ## Verifies if the executed code has a push_error() used @@ -40,7 +56,4 @@ func is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert: ## [codeblock] ## await assert_error().is_push_error() ## [/codeblock] -@warning_ignore("unused_parameter") -func is_push_error(expected_error :String) -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert diff --git a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid index 42642b3f..a33d7fe0 100644 --- a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid @@ -1 +1 @@ -uid://bah37fh61fh7 +uid://nuusjbknya22 diff --git a/addons/gdUnit4/src/GdUnitIntAssert.gd b/addons/gdUnit4/src/GdUnitIntAssert.gd index d593edf9..05eb9223 100644 --- a/addons/gdUnit4/src/GdUnitIntAssert.gd +++ b/addons/gdUnit4/src/GdUnitIntAssert.gd @@ -1,86 +1,79 @@ ## An Assertion Tool to verify integer values -class_name GdUnitIntAssert +@abstract class_name GdUnitIntAssert extends GdUnitAssert -## Verifies that the current String is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitIntAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitIntAssert -## Verifies that the current String is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitIntAssert: - return self + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitIntAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitIntAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitIntAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitIntAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitIntAssert ## Verifies that the current value is less than the given one. -@warning_ignore("unused_parameter") -func is_less(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_less(expected: int) -> GdUnitIntAssert ## Verifies that the current value is less than or equal the given one. -@warning_ignore("unused_parameter") -func is_less_equal(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_less_equal(expected: int) -> GdUnitIntAssert ## Verifies that the current value is greater than the given one. -@warning_ignore("unused_parameter") -func is_greater(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_greater(expected: int) -> GdUnitIntAssert ## Verifies that the current value is greater than or equal the given one. -@warning_ignore("unused_parameter") -func is_greater_equal(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_greater_equal(expected: int) -> GdUnitIntAssert ## Verifies that the current value is even. -func is_even() -> GdUnitIntAssert: - return self +@abstract func is_even() -> GdUnitIntAssert ## Verifies that the current value is odd. -func is_odd() -> GdUnitIntAssert: - return self +@abstract func is_odd() -> GdUnitIntAssert ## Verifies that the current value is negative. -func is_negative() -> GdUnitIntAssert: - return self +@abstract func is_negative() -> GdUnitIntAssert ## Verifies that the current value is not negative. -func is_not_negative() -> GdUnitIntAssert: - return self +@abstract func is_not_negative() -> GdUnitIntAssert ## Verifies that the current value is equal to zero. -func is_zero() -> GdUnitIntAssert: - return self +@abstract func is_zero() -> GdUnitIntAssert ## Verifies that the current value is not equal to zero. -func is_not_zero() -> GdUnitIntAssert: - return self +@abstract func is_not_zero() -> GdUnitIntAssert ## Verifies that the current value is in the given set of values. -@warning_ignore("unused_parameter") -func is_in(expected :Array) -> GdUnitIntAssert: - return self +@abstract func is_in(expected: Array) -> GdUnitIntAssert ## Verifies that the current value is not in the given set of values. -@warning_ignore("unused_parameter") -func is_not_in(expected :Array) -> GdUnitIntAssert: - return self +@abstract func is_not_in(expected: Array) -> GdUnitIntAssert ## Verifies that the current value is between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_between(from :int, to :int) -> GdUnitIntAssert: - return self +@abstract func is_between(from: int, to: int) -> GdUnitIntAssert diff --git a/addons/gdUnit4/src/GdUnitIntAssert.gd.uid b/addons/gdUnit4/src/GdUnitIntAssert.gd.uid index 381494f6..b1466217 100644 --- a/addons/gdUnit4/src/GdUnitIntAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitIntAssert.gd.uid @@ -1 +1 @@ -uid://b1lro4edd12j1 +uid://cw473vl15mnne diff --git a/addons/gdUnit4/src/GdUnitObjectAssert.gd b/addons/gdUnit4/src/GdUnitObjectAssert.gd index 9ce234ce..9d7e76ea 100644 --- a/addons/gdUnit4/src/GdUnitObjectAssert.gd +++ b/addons/gdUnit4/src/GdUnitObjectAssert.gd @@ -1,61 +1,51 @@ ## An Assertion Tool to verify Object values -class_name GdUnitObjectAssert +@abstract class_name GdUnitObjectAssert extends GdUnitAssert -## Verifies that the current object is equal to expected one. -@warning_ignore("unused_parameter") -func is_equal(expected: Variant) -> GdUnitObjectAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitObjectAssert -## Verifies that the current object is not equal to expected one. -@warning_ignore("unused_parameter") -func is_not_equal(expected: Variant) -> GdUnitObjectAssert: - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitObjectAssert -## Verifies that the current object is null. -func is_null() -> GdUnitObjectAssert: - return self +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitObjectAssert -## Verifies that the current object is not null. -func is_not_null() -> GdUnitObjectAssert: - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitObjectAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitObjectAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitObjectAssert ## Verifies that the current object is the same as the given one. -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_same(expected: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_same(expected: Variant) -> GdUnitObjectAssert ## Verifies that the current object is not the same as the given one. -@warning_ignore("unused_parameter") -func is_not_same(expected: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_not_same(expected: Variant) -> GdUnitObjectAssert ## Verifies that the current object is an instance of the given type. -@warning_ignore("unused_parameter") -func is_instanceof(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_instanceof(type: Variant) -> GdUnitObjectAssert ## Verifies that the current object is not an instance of the given type. -@warning_ignore("unused_parameter") -func is_not_instanceof(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_not_instanceof(type: Variant) -> GdUnitObjectAssert ## Checks whether the current object inherits from the specified type. -@warning_ignore("unused_parameter") -func is_inheriting(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_inheriting(type: Variant) -> GdUnitObjectAssert ## Checks whether the current object does NOT inherit from the specified type. -@warning_ignore("unused_parameter") -func is_not_inheriting(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_not_inheriting(type: Variant) -> GdUnitObjectAssert diff --git a/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid b/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid index f095bb5a..8b27d581 100644 --- a/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid @@ -1 +1 @@ -uid://dijc8tb0e1s2s +uid://cpcshb70ud1so diff --git a/addons/gdUnit4/src/GdUnitResultAssert.gd b/addons/gdUnit4/src/GdUnitResultAssert.gd index 347e6374..01eb8800 100644 --- a/addons/gdUnit4/src/GdUnitResultAssert.gd +++ b/addons/gdUnit4/src/GdUnitResultAssert.gd @@ -1,45 +1,51 @@ ## An Assertion Tool to verify Results -class_name GdUnitResultAssert +@abstract class_name GdUnitResultAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitResultAssert: - return self +@abstract func is_null() -> GdUnitResultAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitResultAssert: - return self +@abstract func is_not_null() -> GdUnitResultAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitResultAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitResultAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitResultAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitResultAssert ## Verifies that the result is ends up with empty -func is_empty() -> GdUnitResultAssert: - return self +@abstract func is_empty() -> GdUnitResultAssert ## Verifies that the result is ends up with success -func is_success() -> GdUnitResultAssert: - return self +@abstract func is_success() -> GdUnitResultAssert ## Verifies that the result is ends up with warning -func is_warning() -> GdUnitResultAssert: - return self +@abstract func is_warning() -> GdUnitResultAssert ## Verifies that the result is ends up with error -func is_error() -> GdUnitResultAssert: - return self +@abstract func is_error() -> GdUnitResultAssert ## Verifies that the result contains the given message -@warning_ignore("unused_parameter") -func contains_message(expected :String) -> GdUnitResultAssert: - return self +@abstract func contains_message(expected: String) -> GdUnitResultAssert ## Verifies that the result contains the given value -@warning_ignore("unused_parameter") -func is_value(expected :Variant) -> GdUnitResultAssert: - return self +@abstract func is_value(expected: Variant) -> GdUnitResultAssert diff --git a/addons/gdUnit4/src/GdUnitResultAssert.gd.uid b/addons/gdUnit4/src/GdUnitResultAssert.gd.uid index cba39678..39aeaf35 100644 --- a/addons/gdUnit4/src/GdUnitResultAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitResultAssert.gd.uid @@ -1 +1 @@ -uid://dag1w7wnanxyd +uid://bcdalxbo6bnku diff --git a/addons/gdUnit4/src/GdUnitSceneRunner.gd b/addons/gdUnit4/src/GdUnitSceneRunner.gd index 184be50a..6b11918d 100644 --- a/addons/gdUnit4/src/GdUnitSceneRunner.gd +++ b/addons/gdUnit4/src/GdUnitSceneRunner.gd @@ -1,31 +1,26 @@ ## The Scene Runner is a tool used for simulating interactions on a scene. ## With this tool, you can simulate input events such as keyboard or mouse input and/or simulate scene processing over a certain number of frames. ## This tool is typically used for integration testing a scene. -class_name GdUnitSceneRunner +@abstract class_name GdUnitSceneRunner extends RefCounted -const NO_ARG = GdUnitConstants.NO_ARG - ## Simulates that an action has been pressed.[br] ## [member action] : the action e.g. [code]"ui_up"[/code][br] -@warning_ignore("unused_parameter") -func simulate_action_pressed(action: String) -> GdUnitSceneRunner: - return self +## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br] +@abstract func simulate_action_pressed(action: String, event_index := -1) -> GdUnitSceneRunner ## Simulates that an action is pressed.[br] ## [member action] : the action e.g. [code]"ui_up"[/code][br] -@warning_ignore("unused_parameter") -func simulate_action_press(action: String) -> GdUnitSceneRunner: - return self +## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br] +@abstract func simulate_action_press(action: String, event_index := -1) -> GdUnitSceneRunner ## Simulates that an action has been released.[br] ## [member action] : the action e.g. [code]"ui_up"[/code][br] -@warning_ignore("unused_parameter") -func simulate_action_release(action: String) -> GdUnitSceneRunner: - return self +## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br] +@abstract func simulate_action_release(action: String, event_index := -1) -> GdUnitSceneRunner ## Simulates that a key has been pressed.[br] @@ -37,59 +32,39 @@ func simulate_action_release(action: String) -> GdUnitSceneRunner: ## var runner = scene_runner("res://scenes/simple_scene.tscn") ## await runner.simulate_key_pressed(KEY_SPACE) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner ## Simulates that a key is pressed.[br] ## [member key_code] : the key code e.g. [constant KEY_ENTER][br] ## [member shift_pressed] : false by default set to true if simmulate shift is press[br] ## [member ctrl_pressed] : false by default set to true if simmulate control is press[br] -@warning_ignore("unused_parameter") -func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner ## Simulates that a key has been released.[br] ## [member key_code] : the key code e.g. [constant KEY_ENTER][br] ## [member shift_pressed] : false by default set to true if simmulate shift is press[br] ## [member ctrl_pressed] : false by default set to true if simmulate control is press[br] -@warning_ignore("unused_parameter") -func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: - return self - - -## Sets the mouse cursor to given position relative to the viewport. -## @deprecated: Use [set_mouse_position] instead. -@warning_ignore("unused_parameter") -func set_mouse_pos(position: Vector2) -> GdUnitSceneRunner: - return self +@abstract func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner ## Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the currently focused Window Manager game window.[br] ## [member position] : The absolute position in pixels as Vector2 -@warning_ignore("unused_parameter") -func set_mouse_position(position: Vector2) -> GdUnitSceneRunner: - return self +@abstract func set_mouse_position(position: Vector2) -> GdUnitSceneRunner ## Returns the mouse's position in this Viewport using the coordinate system of this Viewport. -func get_mouse_position() -> Vector2: - return Vector2.ZERO +@abstract func get_mouse_position() -> Vector2 ## Gets the current global mouse position of the current window -func get_global_mouse_position() -> Vector2: - return Vector2.ZERO +@abstract func get_global_mouse_position() -> Vector2 ## Simulates a mouse moved to final position.[br] ## [member position] : The final mouse position -@warning_ignore("unused_parameter") -func simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner ## Simulates a mouse move to the relative coordinates (offset).[br] @@ -103,10 +78,7 @@ func simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner: ## var runner = scene_runner("res://scenes/simple_scene.tscn") ## await runner.simulate_mouse_move_relative(Vector2(100,100)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a mouse move to the absolute coordinates.[br] @@ -120,59 +92,44 @@ func simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_ty ## var runner = scene_runner("res://scenes/simple_scene.tscn") ## await runner.simulate_mouse_move_absolute(Vector2(100,100)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_mouse_move_absolute(position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_mouse_move_absolute(position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a mouse button pressed.[br] ## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants. ## [member double_click] : Set to true to simulate a double-click -@warning_ignore("unused_parameter") -func simulate_mouse_button_pressed(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_button_pressed(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner ## Simulates a mouse button press (holding)[br] ## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants. ## [member double_click] : Set to true to simulate a double-click -@warning_ignore("unused_parameter") -func simulate_mouse_button_press(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_button_press(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner ## Simulates a mouse button released.[br] ## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants. -@warning_ignore("unused_parameter") -func simulate_mouse_button_release(button_index: MouseButton) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_button_release(button_index: MouseButton) -> GdUnitSceneRunner ## Simulates a screen touch is pressed.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member position] : The position to touch the screen.[br] ## [member double_tap] : If true, the touch's state is a double tab. -@warning_ignore("unused_parameter") -func simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner ## Simulates a screen touch press without releasing it immediately, effectively simulating a "hold" action.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member position] : The position to touch the screen.[br] ## [member double_tap] : If true, the touch's state is a double tab. -@warning_ignore("unused_parameter") -func simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner ## Simulates a screen touch is released.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member double_tap] : If true, the touch's state is a double tab. -@warning_ignore("unused_parameter") -func simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSceneRunner ## Simulates a touch drag and drop event to a relative position.[br] @@ -190,10 +147,7 @@ func simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSce ## # and drop it at final at 150,50 relative (50,50 + 100,0) ## await runner.simulate_screen_touch_drag_relative(1, Vector2(100,0)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a touch screen drop to the absolute coordinates (offset).[br] @@ -211,10 +165,7 @@ func simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: fl ## # and drop it at 100,50 ## await runner.simulate_screen_touch_drag_absolute(1, Vector2(100,50)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a complete drag and drop event from one position to another.[br] @@ -232,91 +183,47 @@ func simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: fl ## # start drag at position 50,50 and drop it at 100,50 ## await runner.simulate_screen_touch_drag_drop(1, Vector2(50, 50), Vector2(100,50)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag_drop(index: int, position: Vector2, drop_position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_screen_touch_drag_drop(index: int, position: Vector2, drop_position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a touch screen drag event to given position.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member position] : The drag start position, indicating the drag position.[br] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag(index: int, position: Vector2) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_drag(index: int, position: Vector2) -> GdUnitSceneRunner ## Returns the actual position of the touchscreen drag position by given index. ## [member index] : The touch index in the case of a multi-touch event.[br] -@warning_ignore("unused_parameter") -func get_screen_touch_drag_position(index: int) -> Vector2: - return Vector2.ZERO +@abstract func get_screen_touch_drag_position(index: int) -> Vector2 ## Sets how fast or slow the scene simulation is processed (clock ticks versus the real).[br] ## It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life, ## whilst a value of 0.5 means the game moves at half the regular speed. - - -## Sets the time factor for the scene simulation. ## [member time_factor] : A float representing the simulation speed.[br] ## - Default is 1.0, meaning the simulation runs at normal speed.[br] ## - A value of 2.0 means the simulation runs twice as fast as real time.[br] ## - A value of 0.5 means the simulation runs at half the regular speed.[br] -@warning_ignore("unused_parameter") -func set_time_factor(time_factor: float = 1.0) -> GdUnitSceneRunner: - return self +@abstract func set_time_factor(time_factor: float = 1.0) -> GdUnitSceneRunner ## Simulates scene processing for a certain number of frames.[br] ## [member frames] : amount of frames to process[br] ## [member delta_milli] : the time delta between a frame in milliseconds -@warning_ignore("unused_parameter") -func simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner ## Simulates scene processing until the given signal is emitted by the scene.[br] ## [member signal_name] : the signal to stop the simulation[br] ## [member args] : optional signal arguments to be matched for stop[br] -@warning_ignore("unused_parameter") -func simulate_until_signal( - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_until_signal(signal_name: String, ...args: Array) -> GdUnitSceneRunner ## Simulates scene processing until the given signal is emitted by the given object.[br] ## [member source] : the object that should emit the signal[br] ## [member signal_name] : the signal to stop the simulation[br] ## [member args] : optional signal arguments to be matched for stop -@warning_ignore("unused_parameter") -func simulate_until_object_signal( - source: Object, - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_until_object_signal(source: Object, signal_name: String, ...args: Array) -> GdUnitSceneRunner ## Waits for all input events to be processed by flushing any buffered input events @@ -331,11 +238,7 @@ func simulate_until_object_signal( ## [codeblock] ## await await_input_processed() # Ensure all inputs are processed before continuing ## [/codeblock] -func await_input_processed() -> void: - if scene() != null and scene().process_mode != Node.PROCESS_MODE_DISABLED: - Input.flush_buffered_events() - await (Engine.get_main_loop() as SceneTree).process_frame - await (Engine.get_main_loop() as SceneTree).physics_frame +@abstract func await_input_processed() -> void ## The await_func function pauses execution until a specified function in the scene returns a value.[br] @@ -348,10 +251,7 @@ func await_input_processed() -> void: ## # Waits for 'calculate_score' function and verifies the result is equal to 100. ## await_func("calculate_score").is_equal(100) ## [/codeblock] -@warning_ignore("unused_parameter") -func await_func(func_name: String, args := []) -> GdUnitFuncAssert: - return null - +@abstract func await_func(func_name: String, ...args: Array) -> GdUnitFuncAssert ## The await_func_on function extends the functionality of await_func by allowing you to specify a source node within the scene.[br] @@ -366,19 +266,14 @@ func await_func(func_name: String, args := []) -> GdUnitFuncAssert: ## var my_instance := ScoreCalculator.new() ## await_func(my_instance, "calculate_score").is_equal(100) ## [/codeblock] -@warning_ignore("unused_parameter") -func await_func_on(source: Object, func_name: String, args := []) -> GdUnitFuncAssert: - return null +@abstract func await_func_on(source: Object, func_name: String, ...args: Array) -> GdUnitFuncAssert ## Waits for the specified signal to be emitted by the scene. If the signal is not emitted within the given timeout, the operation fails.[br] ## [member signal_name] : The name of the signal to wait for[br] ## [member args] : The signal arguments as an array[br] ## [member timeout] : The maximum duration (in milliseconds) to wait for the signal to be emitted before failing -@warning_ignore("unused_parameter") -func await_signal(signal_name: String, args := [], timeout := 2000 ) -> void: - await (Engine.get_main_loop() as SceneTree).process_frame - pass +@abstract func await_signal(signal_name: String, args := [], timeout := 2000 ) -> void ## Waits for the specified signal to be emitted by a particular source node. If the signal is not emitted within the given timeout, the operation fails.[br] @@ -386,69 +281,45 @@ func await_signal(signal_name: String, args := [], timeout := 2000 ) -> void: ## [member signal_name] : The name of the signal to wait for[br] ## [member args] : The signal arguments as an array[br] ## [member timeout] : tThe maximum duration (in milliseconds) to wait for the signal to be emitted before failing -@warning_ignore("unused_parameter") -func await_signal_on(source: Object, signal_name: String, args := [], timeout := 2000 ) -> void: - pass +@abstract func await_signal_on(source: Object, signal_name: String, args := [], timeout := 2000 ) -> void ## Restores the scene window to a windowed mode and brings it to the foreground.[br] ## This ensures that the scene is visible and active during testing, making it easier to observe and interact with. -func move_window_to_foreground() -> GdUnitSceneRunner: - return self +@abstract func move_window_to_foreground() -> GdUnitSceneRunner -## Restores the scene window to a windowed mode and brings it to the foreground.[br] -## This ensures that the scene is visible and active during testing, making it easier to observe and interact with. -## @deprecated: Use [move_window_to_foreground] instead. -func maximize_view() -> GdUnitSceneRunner: - return self +## Minimizes the scene window to a windowed mode and brings it to the background.[br] +## This ensures that the scene is hidden during testing. +@abstract func move_window_to_background() -> GdUnitSceneRunner ## Return the current value of the property with the name .[br] ## [member name] : name of property[br] ## [member return] : the value of the property -@warning_ignore("unused_parameter") -func get_property(name: String) -> Variant: - return null +@abstract func get_property(name: String) -> Variant + ## Set the value of the property with the name .[br] ## [member name] : name of property[br] ## [member value] : value of property[br] ## [member return] : true|false depending on valid property name. -@warning_ignore("unused_parameter") -func set_property(name: String, value: Variant) -> bool: - return false +@abstract func set_property(name: String, value: Variant) -> bool ## executes the function specified by in the scene and returns the result.[br] ## [member name] : the name of the function to execute[br] ## [member args] : optional function arguments[br] ## [member return] : the function result -@warning_ignore("unused_parameter") -func invoke( - name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> Variant: - return null +@abstract func invoke(name: String, ...args: Array) -> Variant ## Searches for the specified node with the name in the current scene and returns it, otherwise null.[br] ## [member name] : the name of the node to find[br] ## [member recursive] : enables/disables seraching recursive[br] ## [member return] : the node if find otherwise null -@warning_ignore("unused_parameter") -func find_child(name: String, recursive: bool = true, owned: bool = false) -> Node: - return null +@abstract func find_child(name: String, recursive: bool = true, owned: bool = false) -> Node ## Access to current running scene -func scene() -> Node: - return null +@abstract func scene() -> Node diff --git a/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid b/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid index 34f86e2b..63a9cbde 100644 --- a/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid +++ b/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid @@ -1 +1 @@ -uid://dvr3sbf0r4wbg +uid://cy7xlwa127qcn diff --git a/addons/gdUnit4/src/GdUnitSignalAssert.gd b/addons/gdUnit4/src/GdUnitSignalAssert.gd index 9dbc76d3..bb975e89 100644 --- a/addons/gdUnit4/src/GdUnitSignalAssert.gd +++ b/addons/gdUnit4/src/GdUnitSignalAssert.gd @@ -1,38 +1,46 @@ ## An Assertion Tool to verify for emitted signals until a waiting time -class_name GdUnitSignalAssert +@abstract class_name GdUnitSignalAssert extends GdUnitAssert -## Verifies that given signal is emitted until waiting time -@warning_ignore("unused_parameter") -func is_emitted(name :String, args := []) -> GdUnitSignalAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitSignalAssert -## Verifies that given signal is NOT emitted until waiting time -@warning_ignore("unused_parameter") -func is_not_emitted(name :String, args := []) -> GdUnitSignalAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitSignalAssert -## Verifies the signal exists checked the emitter -@warning_ignore("unused_parameter") -func is_signal_exists(name :String) -> GdUnitSignalAssert: - return self +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitSignalAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitSignalAssert ## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitSignalAssert: - return self +@abstract func override_failure_message(message: String) -> GdUnitSignalAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitSignalAssert + + +## Verifies that given signal is emitted until waiting time +@abstract func is_emitted(name: String, args := []) -> GdUnitSignalAssert + + +## Verifies that given signal is NOT emitted until waiting time +@abstract func is_not_emitted(name: String, args := []) -> GdUnitSignalAssert + + +## Verifies the signal exists checked the emitter +@abstract func is_signal_exists(name: String) -> GdUnitSignalAssert ## Sets the assert signal timeout in ms, if the time over a failure is reported.[br] ## e.g.[br] ## do wait until 5s the instance has emitted the signal `signal_a`[br] ## [code]assert_signal(instance).wait_until(5000).is_emitted("signal_a")[/code] -@warning_ignore("unused_parameter") -func wait_until(timeout :int) -> GdUnitSignalAssert: - return self +@abstract func wait_until(timeout: int) -> GdUnitSignalAssert diff --git a/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid b/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid index 64e8d032..527c77a2 100644 --- a/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid @@ -1 +1 @@ -uid://br73uikcxoydt +uid://xb1bwhjn6qhd diff --git a/addons/gdUnit4/src/GdUnitStringAssert.gd b/addons/gdUnit4/src/GdUnitStringAssert.gd index 5b4a6a1e..2de698bd 100644 --- a/addons/gdUnit4/src/GdUnitStringAssert.gd +++ b/addons/gdUnit4/src/GdUnitStringAssert.gd @@ -1,79 +1,71 @@ ## An Assertion Tool to verify String values -class_name GdUnitStringAssert +@abstract class_name GdUnitStringAssert extends GdUnitAssert -## Verifies that the current String is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitStringAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitStringAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitStringAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitStringAssert ## Verifies that the current String is equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert: - return self +@abstract func is_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert -## Verifies that the current String is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitStringAssert: - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitStringAssert ## Verifies that the current String is not equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_not_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert: - return self +@abstract func is_not_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitStringAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitStringAssert ## Verifies that the current String is empty, it has a length of 0. -func is_empty() -> GdUnitStringAssert: - return self +@abstract func is_empty() -> GdUnitStringAssert ## Verifies that the current String is not empty, it has a length of minimum 1. -func is_not_empty() -> GdUnitStringAssert: - return self +@abstract func is_not_empty() -> GdUnitStringAssert ## Verifies that the current String contains the given String. -@warning_ignore("unused_parameter") -func contains(expected: String) -> GdUnitStringAssert: - return self +@abstract func contains(expected: String) -> GdUnitStringAssert ## Verifies that the current String does not contain the given String. -@warning_ignore("unused_parameter") -func not_contains(expected: String) -> GdUnitStringAssert: - return self +@abstract func not_contains(expected: String) -> GdUnitStringAssert ## Verifies that the current String does not contain the given String, ignoring case considerations. -@warning_ignore("unused_parameter") -func contains_ignoring_case(expected: String) -> GdUnitStringAssert: - return self +@abstract func contains_ignoring_case(expected: String) -> GdUnitStringAssert ## Verifies that the current String does not contain the given String, ignoring case considerations. -@warning_ignore("unused_parameter") -func not_contains_ignoring_case(expected: String) -> GdUnitStringAssert: - return self +@abstract func not_contains_ignoring_case(expected: String) -> GdUnitStringAssert ## Verifies that the current String starts with the given prefix. -@warning_ignore("unused_parameter") -func starts_with(expected: String) -> GdUnitStringAssert: - return self +@abstract func starts_with(expected: String) -> GdUnitStringAssert ## Verifies that the current String ends with the given suffix. -@warning_ignore("unused_parameter") -func ends_with(expected: String) -> GdUnitStringAssert: - return self +@abstract func ends_with(expected: String) -> GdUnitStringAssert ## Verifies that the current String has the expected length by used comparator. -@warning_ignore("unused_parameter") -func has_length(length: int, comparator: int = Comparator.EQUAL) -> GdUnitStringAssert: - return self +@abstract func has_length(length: int, comparator: int = Comparator.EQUAL) -> GdUnitStringAssert diff --git a/addons/gdUnit4/src/GdUnitStringAssert.gd.uid b/addons/gdUnit4/src/GdUnitStringAssert.gd.uid index 0c0dc0b4..e8dc5e29 100644 --- a/addons/gdUnit4/src/GdUnitStringAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitStringAssert.gd.uid @@ -1 +1 @@ -uid://0bkcuwehop6b +uid://iearau013mx4 diff --git a/addons/gdUnit4/src/GdUnitTestSuite.gd b/addons/gdUnit4/src/GdUnitTestSuite.gd index b6790efa..e69c7a8d 100644 --- a/addons/gdUnit4/src/GdUnitTestSuite.gd +++ b/addons/gdUnit4/src/GdUnitTestSuite.gd @@ -161,7 +161,7 @@ func resource_as_var(resource_path :String) -> Variant: return str_to_var(__gdunit_file_access().resource_as_string(resource_path) as String) -## Waits for given signal is emited by the until a specified timeout to fail[br] +## Waits for given signal to be emitted by until a specified timeout to fail[br] ## source: the object from which the signal is emitted[br] ## signal_name: signal name[br] ## args: the expected signal arguments as an array[br] @@ -177,7 +177,7 @@ func await_idle_frame() -> void: await __awaiter.await_idle_frame() -## Waits for for a given amount of milliseconds[br] +## Waits for a given amount of milliseconds[br] ## example:[br] ## [codeblock] ## # waits for 100ms @@ -307,7 +307,7 @@ func any_float() -> GdUnitArgumentMatcher: return __gdunit_argument_matchers().by_type(TYPE_FLOAT) -## Argument matcher to match any string value +## Argument matcher to match any String value func any_string() -> GdUnitArgumentMatcher: @warning_ignore("unsafe_method_access") return __gdunit_argument_matchers().by_type(TYPE_STRING) @@ -362,7 +362,7 @@ func any_vector4() -> GdUnitArgumentMatcher: return __gdunit_argument_matchers().by_type(TYPE_VECTOR4) -## Argument matcher to match any Vector3i value +## Argument matcher to match any Vector4i value func any_vector4i() -> GdUnitArgumentMatcher: @warning_ignore("unsafe_method_access") return __gdunit_argument_matchers().by_type(TYPE_VECTOR4I) @@ -607,7 +607,7 @@ func assert_func(instance :Object, func_name :String, args := Array()) -> GdUnit return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd").new(instance, func_name, args) -## An Assertion Tool to verify for emitted signals until a certain time. +## An assertion tool to verify for emitted signals until a certain time. func assert_signal(instance :Object) -> GdUnitSignalAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd").new(instance) @@ -636,15 +636,15 @@ func assert_failure_await(assertion :Callable) -> GdUnitFailureAssert: return await __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute_and_await(assertion) -## An assertion tool to verify for Godot errors.[br] -## You can use to verify for certain Godot erros like failing assertions, push_error, push_warn.[br] +## An assertion tool to verify Godot errors.[br] +## You can use to verify certain Godot errors like failing assertions, push_error, push_warn.[br] ## Usage: ## [codeblock] -## # tests no error was occured during execution the code +## # tests no error occurred during execution of the code ## await assert_error(func (): return 0 )\ ## .is_success() ## -## # tests an push_error('test error') was occured during execution the code +## # tests a push_error('test error') occured during execution of the code ## await assert_error(func (): push_error('test error') )\ ## .is_push_error('test error') ## [/codeblock] @@ -652,12 +652,36 @@ func assert_error(current :Callable) -> GdUnitGodotErrorAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd").new(current) +## Explicitly fails the current test indicating that the feature is not yet implemented.[br] +## This function is useful during development when you want to write test cases before implementing the actual functionality.[br] +## It provides a clear indication that the test failure is expected because the feature is still under development.[br] +## Usage: +## [codeblock] +## # Test for a feature that will be implemented later +## func test_advanced_ai_behavior(): +## assert_not_yet_implemented() +## +## [/codeblock] func assert_not_yet_implemented() -> void: @warning_ignore("unsafe_method_access") __gdunit_assert().new(null).do_fail() -func fail(message :String) -> void: +## Explicitly fails the current test with a custom error message.[br] +## This function reports an error but does not terminate test execution automatically.[br] +## You must use 'return' after calling fail() to stop the test since GDScript has no exception support.[br] +## Useful for complex conditional testing scenarios where standard assertions are insufficient.[br] +## Usage: +## [codeblock] +## # Fail test when conditions are not met +## if !custom_check(player): +## fail("Player should be alive but has %d health" % player.health) +## return +## +## # Continue with test if conditions pass +## assert_that(player.health).is_greater(0) +## [/codeblock] +func fail(message: String) -> void: @warning_ignore("unsafe_method_access") __gdunit_assert().new(null).report_error(message) diff --git a/addons/gdUnit4/src/GdUnitTestSuite.gd.uid b/addons/gdUnit4/src/GdUnitTestSuite.gd.uid index b4bb12b4..c80fe57e 100644 --- a/addons/gdUnit4/src/GdUnitTestSuite.gd.uid +++ b/addons/gdUnit4/src/GdUnitTestSuite.gd.uid @@ -1 +1 @@ -uid://8uu53dqxpmgo +uid://f7s2ku6xflsp diff --git a/addons/gdUnit4/src/GdUnitTuple.gd.uid b/addons/gdUnit4/src/GdUnitTuple.gd.uid index a9bc7830..53bd4700 100644 --- a/addons/gdUnit4/src/GdUnitTuple.gd.uid +++ b/addons/gdUnit4/src/GdUnitTuple.gd.uid @@ -1 +1 @@ -uid://bc10yd555y80s +uid://bixlow0k4kct2 diff --git a/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid b/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid index 6f7d76f1..db002932 100644 --- a/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid +++ b/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid @@ -1 +1 @@ -uid://bu03gvojqe8it +uid://b8u2uhho2e2ts diff --git a/addons/gdUnit4/src/GdUnitVectorAssert.gd b/addons/gdUnit4/src/GdUnitVectorAssert.gd index 915fd3b6..c186cba2 100644 --- a/addons/gdUnit4/src/GdUnitVectorAssert.gd +++ b/addons/gdUnit4/src/GdUnitVectorAssert.gd @@ -1,57 +1,55 @@ ## An Assertion Tool to verify Vector values -class_name GdUnitVectorAssert +@abstract class_name GdUnitVectorAssert extends GdUnitAssert -## Verifies that the current value is equal to expected one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitVectorAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitVectorAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitVectorAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is not equal to expected one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current and expected value are approximately equal. -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_equal_approx(expected :Variant, approx :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_equal_approx(expected: Variant, approx: Variant) -> GdUnitVectorAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitVectorAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitVectorAssert ## Verifies that the current value is less than the given one. -@warning_ignore("unused_parameter") -func is_less(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_less(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is less than or equal the given one. -@warning_ignore("unused_parameter") -func is_less_equal(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_less_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is greater than the given one. -@warning_ignore("unused_parameter") -func is_greater(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_greater(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is greater than or equal the given one. -@warning_ignore("unused_parameter") -func is_greater_equal(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_greater_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_between(from :Variant, to :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_between(from: Variant, to: Variant) -> GdUnitVectorAssert ## Verifies that the current value is not between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_not_between(from :Variant, to :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_not_between(from: Variant, to: Variant) -> GdUnitVectorAssert diff --git a/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid b/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid index a57392c2..dd449ebe 100644 --- a/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid @@ -1 +1 @@ -uid://brmfcibwgsahu +uid://dnpcv7np161w6 diff --git a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid index 0e49316b..e5f80fdd 100644 --- a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid +++ b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid @@ -1 +1 @@ -uid://cdl7jtk6eoo47 +uid://bges5joxsvybn diff --git a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid index cd41b76e..66a0a26e 100644 --- a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid +++ b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid @@ -1 +1 @@ -uid://cnxrld6ntnh7u +uid://cmu5qjg1vgdm5 diff --git a/addons/gdUnit4/src/asserts/GdAssertMessages.gd b/addons/gdUnit4/src/asserts/GdAssertMessages.gd index 2043e086..abc135a1 100644 --- a/addons/gdUnit4/src/asserts/GdAssertMessages.gd +++ b/addons/gdUnit4/src/asserts/GdAssertMessages.gd @@ -58,17 +58,17 @@ static func input_event_as_text(event :InputEvent) -> String: return text -static func _colored_string_div(characters :String) -> String: - return colored_array_div(characters.to_utf8_buffer()) +static func _colored_string_div(characters: String) -> String: + return colored_array_div(characters.to_utf32_buffer().to_int32_array()) -static func colored_array_div(characters :PackedByteArray) -> String: +static func colored_array_div(characters: PackedInt32Array) -> String: if characters.is_empty(): return "" - var result := PackedByteArray() + var result := PackedInt32Array() var index := 0 - var missing_chars := PackedByteArray() - var additional_chars := PackedByteArray() + var missing_chars := PackedInt32Array() + var additional_chars := PackedInt32Array() while index < characters.size(): var character := characters[index] @@ -84,17 +84,17 @@ static func colored_array_div(characters :PackedByteArray) -> String: _: if not missing_chars.is_empty(): result.append_array(format_chars(missing_chars, SUB_COLOR)) - missing_chars = PackedByteArray() + missing_chars = PackedInt32Array() if not additional_chars.is_empty(): result.append_array(format_chars(additional_chars, ADD_COLOR)) - additional_chars = PackedByteArray() + additional_chars = PackedInt32Array() @warning_ignore("return_value_discarded") result.append(character) index += 1 result.append_array(format_chars(missing_chars, SUB_COLOR)) result.append_array(format_chars(additional_chars, ADD_COLOR)) - return result.get_string_from_utf8() + return result.to_byte_array().get_string_from_utf32() static func _typed_value(value :Variant) -> String: @@ -637,12 +637,12 @@ static func error_contains_exactly(current: Array, expected: Array) -> String: return "%s\n %s\n but was\n %s" % [_error("Expecting exactly equal:"), _colored_value(expected), _colored_value(current)] -static func format_chars(characters :PackedByteArray, type :Color) -> PackedByteArray: +static func format_chars(characters: PackedInt32Array, type: Color) -> PackedInt32Array: if characters.size() == 0:# or characters[0] == 10: return characters # Replace each control character with its readable form - var formatted_text := characters.get_string_from_utf8() + var formatted_text := characters.to_byte_array().get_string_from_utf32() for control_char: String in CONTROL_CHARS: var replace_text: String = CONTROL_CHARS[control_char] formatted_text = formatted_text.replace(control_char, replace_text) @@ -664,8 +664,8 @@ static func format_chars(characters :PackedByteArray, type :Color) -> PackedByte ascii_text ] - var result := PackedByteArray() - result.append_array(message.to_utf8_buffer()) + var result := PackedInt32Array() + result.append_array(message.to_utf32_buffer().to_int32_array()) return result diff --git a/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid index 36a457b2..557b6d1a 100644 --- a/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid +++ b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid @@ -1 +1 @@ -uid://bhy2expm2ddxg +uid://14o2ba64s3a0 diff --git a/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid index eed62664..47b96ef8 100644 --- a/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid +++ b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid @@ -1 +1 @@ -uid://cmvwe16tba437 +uid://jc4b0rkbicjq diff --git a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd index 748e20e6..9643cfa1 100644 --- a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd @@ -26,11 +26,13 @@ func _notification(event: int) -> void: func report_success() -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error: String) -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -40,11 +42,13 @@ func failure_message() -> String: func override_failure_message(message: String) -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self func append_failure_message(message: String) -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -109,76 +113,83 @@ func _array_div(compare_mode: GdObjects.COMPARE_MODE, left: Array[Variant], righ return [not_expect, not_found] -func _contains(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var by_reference := compare_mode == GdObjects.COMPARE_MODE.OBJECT_REFERENCE var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null: - return report_error(GdAssertMessages.error_arr_contains(current_value, expected, [], expected, by_reference)) + return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], expected_value, by_reference)) @warning_ignore("unsafe_cast") - var diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant]) + var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant]) #var not_expect := diffs[0] as Array var not_found: Array = diffs[1] if not not_found.is_empty(): - return report_error(GdAssertMessages.error_arr_contains(current_value, expected, [], not_found, by_reference)) + return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], not_found, by_reference)) return report_success() -func _contains_exactly(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _contains_exactly(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null: - return report_error(GdAssertMessages.error_arr_contains_exactly(null, expected, [], expected, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly(null, expected_value, [], expected_value, compare_mode)) # has same content in same order - if _is_equal(current_value, expected, false, compare_mode): + if _is_equal(current_value, expected_value, false, compare_mode): return report_success() # check has same elements but in different order - if _is_equals_sorted(current_value, expected, false, compare_mode): - return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected, [], [], compare_mode)) + if _is_equals_sorted(current_value, expected_value, false, compare_mode): + return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, [], [], compare_mode)) # find the difference @warning_ignore("unsafe_cast") var diffs := _array_div(compare_mode, current_value as Array[Variant], - expected as Array[Variant], + expected_value as Array[Variant], GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) var not_expect: Array[Variant] = diffs[0] var not_found: Array[Variant] = diffs[1] - return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected, not_expect, not_found, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, not_expect, not_found, compare_mode)) -func _contains_exactly_in_any_order(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _contains_exactly_in_any_order(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null: - return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, [], expected, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [], expected_value, compare_mode)) # find the difference @warning_ignore("unsafe_cast") - var diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant], false) + var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant], false) var not_expect: Array[Variant] = diffs[0] var not_found: Array[Variant] = diffs[1] if not_expect.is_empty() and not_found.is_empty(): return report_success() - return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, not_expect, not_found, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, not_expect, not_found, compare_mode)) -func _not_contains(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _not_contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) if current_value == null: - return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, [], expected, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [], expected_value, compare_mode)) @warning_ignore("unsafe_cast") - var diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant]) + var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant]) var found: Array[Variant] = diffs[0] @warning_ignore("unsafe_cast") if found.size() == (current_value as Array).size(): return report_success() @warning_ignore("unsafe_cast") - var diffs2 := _array_div(compare_mode, expected as Array[Variant], diffs[1] as Array[Variant]) - return report_error(GdAssertMessages.error_arr_not_contains(current_value, expected, diffs2[0], compare_mode)) + var diffs2 := _array_div(compare_mode, expected_value as Array[Variant], diffs[1] as Array[Variant]) + return report_error(GdAssertMessages.error_arr_not_contains(current_value, expected_value, diffs2[0], compare_mode)) func is_null() -> GdUnitArrayAssert: @@ -193,15 +204,16 @@ func is_not_null() -> GdUnitArrayAssert: return self -# Verifies that the current String is equal to the given one. -func is_equal(expected: Variant) -> GdUnitArrayAssert: - if _type_check and not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_equal(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if current_value == null and expected != null: - return report_error(GdAssertMessages.error_equal(null, expected)) - if not _is_equal(current_value, expected): - var diff := _array_equals_div(current_value, expected) + var expected_value: Variant= _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null and expected_value != null: + return report_error(GdAssertMessages.error_equal(null, expected_value)) + + if not _is_equal(current_value, expected_value): + var diff := _array_equals_div(current_value, expected_value) var expected_as_list := GdArrayTools.as_string(diff[0], false) var current_as_list := GdArrayTools.as_string(diff[1], false) var index_report: Array = diff[2] @@ -210,16 +222,18 @@ func is_equal(expected: Variant) -> GdUnitArrayAssert: # Verifies that the current Array is equal to the given one, ignoring case considerations. -func is_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert: - if _type_check and not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if current_value == null and expected != null: + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null and expected_value != null: @warning_ignore("unsafe_cast") - return report_error(GdAssertMessages.error_equal(null, GdArrayTools.as_string(expected as Array))) - if not _is_equal(current_value, expected, true): + return report_error(GdAssertMessages.error_equal(null, GdArrayTools.as_string(expected_value))) + + if not _is_equal(current_value, expected_value, true): @warning_ignore("unsafe_cast") - var diff := _array_equals_div(current_value as Array[Variant], expected as Array[Variant], true) + var diff := _array_equals_div(current_value, expected_value, true) var expected_as_list := GdArrayTools.as_string(diff[0]) var current_as_list := GdArrayTools.as_string(diff[1]) var index_report: Array = diff[2] @@ -227,24 +241,28 @@ func is_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert: return report_success() -func is_not_equal(expected: Variant) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_not_equal(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if _is_equal(current_value, expected): - return report_error(GdAssertMessages.error_not_equal(current_value, expected)) + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + + if _is_equal(current_value, expected_value): + return report_error(GdAssertMessages.error_not_equal(current_value, expected_value)) return report_success() -func is_not_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_not_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if _is_equal(current_value, expected, true): + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + + if _is_equal(current_value, expected_value, true): @warning_ignore("unsafe_cast") var c := GdArrayTools.as_string(current_value as Array) @warning_ignore("unsafe_cast") - var e := GdArrayTools.as_string(expected as Array) + var e := GdArrayTools.as_string(expected_value) return report_error(GdAssertMessages.error_not_equal_case_insensetiv(c, e)) return report_success() @@ -294,35 +312,35 @@ func has_size(expected: int) -> GdUnitArrayAssert: return report_success() -func contains(expected: Variant) -> GdUnitArrayAssert: +func contains(...expected: Array) -> GdUnitArrayAssert: return _contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func contains_exactly(expected: Variant) -> GdUnitArrayAssert: +func contains_exactly(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func contains_exactly_in_any_order(expected: Variant) -> GdUnitArrayAssert: +func contains_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func contains_same(expected: Variant) -> GdUnitArrayAssert: +func contains_same(...expected: Array) -> GdUnitArrayAssert: return _contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func contains_same_exactly(expected: Variant) -> GdUnitArrayAssert: +func contains_same_exactly(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func contains_same_exactly_in_any_order(expected: Variant) -> GdUnitArrayAssert: +func contains_same_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func not_contains(expected: Variant) -> GdUnitArrayAssert: +func not_contains(...expected: Array) -> GdUnitArrayAssert: return _not_contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func not_contains_same(expected: Variant) -> GdUnitArrayAssert: +func not_contains_same(...expected: Array) -> GdUnitArrayAssert: return _not_contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) @@ -332,9 +350,9 @@ func is_instanceof(expected: Variant) -> GdUnitAssert: return self -func extract(func_name: String, args := Array()) -> GdUnitArrayAssert: +func extract(func_name: String, ...func_args: Array) -> GdUnitArrayAssert: var extracted_elements := Array() - + var args: Array = _extract_variadic_value(func_args) var extractor := GdUnitFuncValueExtractor.new(func_name, args) var current: Variant = get_current_value() if current == null: @@ -346,18 +364,7 @@ func extract(func_name: String, args := Array()) -> GdUnitArrayAssert: return self -func extractv( - extr0: GdUnitValueExtractor, - extr1: GdUnitValueExtractor = null, - extr2: GdUnitValueExtractor = null, - extr3: GdUnitValueExtractor = null, - extr4: GdUnitValueExtractor = null, - extr5: GdUnitValueExtractor = null, - extr6: GdUnitValueExtractor = null, - extr7: GdUnitValueExtractor = null, - extr8: GdUnitValueExtractor = null, - extr9: GdUnitValueExtractor = null) -> GdUnitArrayAssert: - var extractors: Variant = GdArrayTools.filter_value([extr0, extr1, extr2, extr3, extr4, extr5, extr6, extr7, extr8, extr9], null) +func extractv(...extractors: Array) -> GdUnitArrayAssert: var extracted_elements := Array() var current: Variant = get_current_value() if current == null: @@ -376,12 +383,11 @@ func extractv( GdUnitTuple.NO_ARG, GdUnitTuple.NO_ARG ] - @warning_ignore("unsafe_cast") - for index: int in (extractors as Array).size(): + + for index: int in extractors.size(): var extractor: GdUnitValueExtractor = extractors[index] ev[index] = extractor.extract_value(element) - @warning_ignore("unsafe_cast") - if (extractors as Array).size() > 1: + if extractors.size() > 1: extracted_elements.append(GdUnitTuple.new(ev[0], ev[1], ev[2], ev[3], ev[4], ev[5], ev[6], ev[7], ev[8], ev[9])) else: extracted_elements.append(ev[0]) @@ -389,6 +395,14 @@ func extractv( return self +## Small helper to support the old expected arguments as single array and variadic arguments +func _extract_variadic_value(values: Variant) -> Variant: + @warning_ignore("unsafe_method_access") + if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]): + return values[0] + return values + + @warning_ignore("incompatible_ternary") func _is_equal( left: Variant, diff --git a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid index 45d5b5d9..31155661 100644 --- a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid @@ -1 +1 @@ -uid://j2teqcuf1i2t +uid://cb21qslyay6ej diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd index c08bc132..9f578c4d 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd @@ -42,30 +42,16 @@ func do_fail() -> GdUnitAssert: return report_error(GdAssertMessages.error_not_implemented()) -func override_failure_message(message :String) -> GdUnitAssert: +func override_failure_message(message: String) -> GdUnitAssert: _custom_failure_message = message return self -func append_failure_message(message :String) -> GdUnitAssert: +func append_failure_message(message: String) -> GdUnitAssert: _additional_failure_message = message return self -func is_equal(expected :Variant) -> GdUnitAssert: - var current :Variant = current_value() - if not GdObjects.equals(current, expected): - return report_error(GdAssertMessages.error_equal(current, expected)) - return report_success() - - -func is_not_equal(expected :Variant) -> GdUnitAssert: - var current :Variant = current_value() - if GdObjects.equals(current, expected): - return report_error(GdAssertMessages.error_not_equal(current, expected)) - return report_success() - - func is_null() -> GdUnitAssert: var current :Variant = current_value() if current != null: @@ -78,3 +64,17 @@ func is_not_null() -> GdUnitAssert: if current == null: return report_error(GdAssertMessages.error_is_not_null()) return report_success() + + +func is_equal(expected: Variant) -> GdUnitAssert: + var current: Variant = current_value() + if not GdObjects.equals(current, expected): + return report_error(GdAssertMessages.error_equal(current, expected)) + return report_success() + + +func is_not_equal(expected: Variant) -> GdUnitAssert: + var current: Variant = current_value() + if GdObjects.equals(current, expected): + return report_error(GdAssertMessages.error_not_equal(current, expected)) + return report_success() diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid index 116a364d..f8145e41 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid @@ -1 +1 @@ -uid://ca566qfqfwljx +uid://cp6bxkvkq0f2r diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd index 875fb8ed..a5b53c17 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd +++ b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd @@ -7,6 +7,7 @@ extends RefCounted func _init() -> void: # preload all gdunit assertions to speedup testsuite loading time # gdlint:disable=private-method-call + @warning_ignore_start("return_value_discarded") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd") @@ -22,6 +23,7 @@ func _init() -> void: GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd") + @warning_ignore_restore("return_value_discarded") ### We now load all used asserts and tool scripts into the cache according to the principle of "lazy loading" diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid index 784a6782..5e09a781 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid @@ -1 +1 @@ -uid://dg82pb5wb6qjh +uid://dgl2mjv4s5ynx diff --git a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd index 5daebcec..2fc0ce43 100644 --- a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd @@ -24,11 +24,13 @@ func current_value() -> Variant: func report_success() -> GdUnitBoolAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitBoolAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -37,26 +39,24 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitBoolAssert: +func override_failure_message(message: String) -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitBoolAssert: +func append_failure_message(message: String) -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self -# Verifies that the current value is null. func is_null() -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.is_null() return self -# Verifies that the current value is not null. func is_not_null() -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.is_not_null() diff --git a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid index e993c924..6397800b 100644 --- a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid @@ -1 +1 @@ -uid://ct4fsu5mr4ix8 +uid://c0cj4syxdexuh diff --git a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd index 17eba6ab..6d62dcff 100644 --- a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd @@ -20,11 +20,13 @@ func _notification(event :int) -> void: func report_success() -> GdUnitDictionaryAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitDictionaryAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -33,13 +35,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitDictionaryAssert: +func override_failure_message(message: String) -> GdUnitDictionaryAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitDictionaryAssert: +func append_failure_message(message: String) -> GdUnitDictionaryAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,21 +63,19 @@ func is_not_null() -> GdUnitDictionaryAssert: return self -func is_equal(expected :Variant) -> GdUnitDictionaryAssert: +func is_equal(expected: Variant) -> GdUnitDictionaryAssert: var current :Variant = current_value() if current == null: return report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected))) if not GdObjects.equals(current, expected): var c := GdAssertMessages.format_dict(current) var e := GdAssertMessages.format_dict(expected) - var diff := GdDiffTool.string_diff(c, e) - var curent_diff := GdAssertMessages.colored_array_div(diff[1]) - return report_error(GdAssertMessages.error_equal(curent_diff, e)) + return report_error(GdAssertMessages.error_equal(c, e)) return report_success() -func is_not_equal(expected :Variant) -> GdUnitDictionaryAssert: - var current :Variant = current_value() +func is_not_equal(expected: Variant) -> GdUnitDictionaryAssert: + var current: Variant = current_value() if GdObjects.equals(current, expected): return report_error(GdAssertMessages.error_not_equal(current, expected)) return report_success() @@ -89,9 +89,7 @@ func is_same(expected :Variant) -> GdUnitDictionaryAssert: if not is_same(current, expected): var c := GdAssertMessages.format_dict(current) var e := GdAssertMessages.format_dict(expected) - var diff := GdDiffTool.string_diff(c, e) - var curent_diff := GdAssertMessages.colored_array_div(diff[1]) - return report_error(GdAssertMessages.error_is_same(curent_diff, e)) + return report_error(GdAssertMessages.error_is_same(c, e)) return report_success() @@ -129,16 +127,17 @@ func has_size(expected: int) -> GdUnitDictionaryAssert: return report_success() -func _contains_keys(expected :Array, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: +func _contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: var current :Variant = current_value() + var expected_value: Array = _extract_variadic_value(expected) if current == null: return report_error(GdAssertMessages.error_is_not_null()) # find expected keys @warning_ignore("unsafe_cast") - var keys_not_found :Array = expected.filter(_filter_by_key.bind((current as Dictionary).keys(), compare_mode)) + var keys_not_found :Array = expected_value.filter(_filter_by_key.bind((current as Dictionary).keys(), compare_mode)) if not keys_not_found.is_empty(): @warning_ignore("unsafe_cast") - return report_error(GdAssertMessages.error_contains_keys((current as Dictionary).keys() as Array, expected, keys_not_found, compare_mode)) + return report_error(GdAssertMessages.error_contains_keys((current as Dictionary).keys() as Array, expected_value, keys_not_found, compare_mode)) return report_success() @@ -156,18 +155,19 @@ func _contains_key_value(key :Variant, value :Variant, compare_mode :GdObjects.C return report_success() -func _not_contains_keys(expected :Array, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: +func _not_contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: var current :Variant = current_value() + var expected_value: Array = _extract_variadic_value(expected) if current == null: return report_error(GdAssertMessages.error_is_not_null()) var dict_current: Dictionary = current - var keys_found :Array = dict_current.keys().filter(_filter_by_key.bind(expected, compare_mode, true)) + var keys_found :Array = dict_current.keys().filter(_filter_by_key.bind(expected_value, compare_mode, true)) if not keys_found.is_empty(): - return report_error(GdAssertMessages.error_not_contains_keys(dict_current.keys() as Array, expected, keys_found, compare_mode)) + return report_error(GdAssertMessages.error_not_contains_keys(dict_current.keys() as Array, expected_value, keys_found, compare_mode)) return report_success() -func contains_keys(expected :Array) -> GdUnitDictionaryAssert: +func contains_keys(...expected: Array) -> GdUnitDictionaryAssert: return _contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) @@ -175,7 +175,7 @@ func contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert: return _contains_key_value(key, value, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func not_contains_keys(expected :Array) -> GdUnitDictionaryAssert: +func not_contains_keys(...expected: Array) -> GdUnitDictionaryAssert: return _not_contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) @@ -187,7 +187,7 @@ func contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAs return _contains_key_value(key, value, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func not_contains_same_keys(expected :Array) -> GdUnitDictionaryAssert: +func not_contains_same_keys(...expected: Array) -> GdUnitDictionaryAssert: return _not_contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) @@ -196,3 +196,11 @@ func _filter_by_key(element :Variant, values :Array, compare_mode :GdObjects.COM if GdObjects.equals(key, element, false, compare_mode): return is_not return !is_not + + +## Small helper to support the old expected arguments as single array and variadic arguments +func _extract_variadic_value(values: Variant) -> Variant: + @warning_ignore("unsafe_method_access") + if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]): + return values[0] + return values diff --git a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid index 1aa2ad24..73ae5e77 100644 --- a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid @@ -1 +1 @@ -uid://c02hyg8hu5pqi +uid://c0alu4lo1wnld diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd index 845d5fa9..198624c6 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd @@ -3,7 +3,10 @@ extends GdUnitFailureAssert const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") var _is_failed := false -var _failure_message :String +var _failure_message: String +var _current_failure_message := "" +var _custom_failure_message := "" +var _additional_failure_message := "" func _set_do_expect_fail(enabled :bool = true) -> void: @@ -44,12 +47,10 @@ func _on_test_failed(value :bool) -> void: _is_failed = value -@warning_ignore("unused_parameter") func is_equal(_expected: Variant) -> GdUnitFailureAssert: return _report_error("Not implemented") -@warning_ignore("unused_parameter") func is_not_equal(_expected: Variant) -> GdUnitFailureAssert: return _report_error("Not implemented") @@ -62,6 +63,16 @@ func is_not_null() -> GdUnitFailureAssert: return _report_error("Not implemented") +func override_failure_message(message: String) -> GdUnitFailureAssert: + _custom_failure_message = message + return self + + +func append_failure_message(message: String) -> GdUnitFailureAssert: + _additional_failure_message = message + return self + + func is_success() -> GdUnitFailureAssert: if _is_failed: return _report_error("Expect: assertion ends successfully.") @@ -115,7 +126,8 @@ func starts_with_message(expected :String) -> GdUnitFailureAssert: func _report_error(error_message :String, failure_line_number: int = -1) -> GdUnitAssert: var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number() - GdAssertReports.report_error(error_message, line_number) + _current_failure_message = GdAssertMessages.build_failure_message(error_message, _additional_failure_message, _custom_failure_message) + GdAssertReports.report_error(_current_failure_message, line_number) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid index 0999c6ad..bccb864b 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid @@ -1 +1 @@ -uid://bbbgms0y8xw6r +uid://dlh2gv6obl4h5 diff --git a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd index f98bc933..c4f9570e 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd @@ -26,11 +26,13 @@ func current_value() -> String: func report_success() -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -39,25 +41,37 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitFileAssert: +func override_failure_message(message: String) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitFileAssert: +func append_failure_message(message: String) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self -func is_equal(expected :Variant) -> GdUnitFileAssert: +func is_null() -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") + _base.is_null() + return self + + +func is_not_null() -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") + _base.is_not_null() + return self + + +func is_equal(expected: Variant) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.is_equal(expected) return self -func is_not_equal(expected :Variant) -> GdUnitFileAssert: +func is_not_equal(expected: Variant) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.is_not_equal(expected) return self @@ -97,5 +111,6 @@ func contains_exactly(expected_rows: Array) -> GdUnitFileAssert: if script is GDScript: var source_code := GdScriptParser.to_unix_format(script.source_code) var rows := Array(source_code.split("\n")) + @warning_ignore("return_value_discarded") GdUnitArrayAssertImpl.new(rows).contains_exactly(expected_rows) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid index 4a8d9f9e..be4d3f8c 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid @@ -1 +1 @@ -uid://dgqxit1c7bkn1 +uid://crk42vchmjqt diff --git a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd index 05d05b84..83d7e05e 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd @@ -24,11 +24,13 @@ func current_value() -> Variant: func report_success() -> GdUnitFloatAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitFloatAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -37,13 +39,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitFloatAssert: +func override_failure_message(message: String) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitFloatAssert: +func append_failure_message(message: String) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,13 +63,13 @@ func is_not_null() -> GdUnitFloatAssert: return self -func is_equal(expected :Variant) -> GdUnitFloatAssert: +func is_equal(expected: Variant) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.is_equal(expected) return self -func is_not_equal(expected :Variant) -> GdUnitFloatAssert: +func is_not_equal(expected: Variant) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.is_not_equal(expected) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid index a1bf5bda..3f7adc5d 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid @@ -1 +1 @@ -uid://cuk4jobik71ej +uid://dylxf40ghd5r8 diff --git a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd index 270119a6..c38acf08 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd @@ -59,12 +59,12 @@ func failure_message() -> String: return _current_failure_message -func override_failure_message(message :String) -> GdUnitFuncAssert: +func override_failure_message(message: String) -> GdUnitFuncAssert: _custom_failure_message = message return self -func append_failure_message(message :String) -> GdUnitFuncAssert: +func append_failure_message(message: String) -> GdUnitFuncAssert: _additional_failure_message = message return self @@ -98,12 +98,12 @@ func is_true() -> GdUnitFuncAssert: return self -func is_equal(expected :Variant) -> GdUnitFuncAssert: +func is_equal(expected: Variant) -> GdUnitFuncAssert: await _validate_callback(cb_is_equal, expected) return self -func is_not_equal(expected :Variant) -> GdUnitFuncAssert: +func is_not_equal(expected: Variant) -> GdUnitFuncAssert: await _validate_callback(cb_is_not_equal, expected) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid index a971099a..3d3d7d99 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid @@ -1 +1 @@ -uid://c2dfhrdyv5ji7 +uid://bmrvl1l1og7o1 diff --git a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd index adf2a2eb..fc010db3 100644 --- a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd @@ -1,13 +1,12 @@ extends GdUnitGodotErrorAssert -var _current_error_message :String -var _callable :Callable +var _current_failure_message := "" +var _custom_failure_message := "" +var _additional_failure_message := "" +var _callable: Callable -func _init(callable :Callable) -> void: - # we only support Godot 4.1.x+ because of await issue https://github.com/godotengine/godot/issues/80292 - assert(Engine.get_version_info().hex >= 0x40100, - "This assertion is not supported for Godot 4.0.x. Please upgrade to the minimum version Godot 4.1.0!") +func _init(callable: Callable) -> void: # save the actual assert instance on the current thread context GdUnitThreadManager.get_current_context().set_assert(self) GdAssertReports.reset_last_error_line_number() @@ -29,7 +28,7 @@ func _error_monitor() -> GodotGdErrorMonitor: func failure_message() -> String: - return _current_error_message + return _current_failure_message func _report_success() -> GdUnitAssert: @@ -37,23 +36,23 @@ func _report_success() -> GdUnitAssert: return self -func _report_error(error_message :String, failure_line_number: int = -1) -> GdUnitAssert: +func _report_error(error_message: String, failure_line_number: int = -1) -> GdUnitAssert: var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number() - _current_error_message = error_message - GdAssertReports.report_error(error_message, line_number) + _current_failure_message = GdAssertMessages.build_failure_message(error_message, _additional_failure_message, _custom_failure_message) + GdAssertReports.report_error(_current_failure_message, line_number) return self -func _has_log_entry(log_entries :Array[ErrorLogEntry], type :ErrorLogEntry.TYPE, error :String) -> bool: +func _has_log_entry(log_entries: Array[ErrorLogEntry], type: ErrorLogEntry.TYPE, error: Variant) -> bool: for entry in log_entries: - if entry._type == type and entry._message == error: + if entry._type == type and GdObjects.equals(entry._message, error): # Erase the log entry we already handled it by this assertion, otherwise it will report at twice _error_monitor().erase_log_entry(entry) return true return false -func _to_list(log_entries :Array[ErrorLogEntry]) -> String: +func _to_list(log_entries: Array[ErrorLogEntry]) -> String: if log_entries.is_empty(): return "no errors" if log_entries.size() == 1: @@ -64,6 +63,32 @@ func _to_list(log_entries :Array[ErrorLogEntry]) -> String: return value +func is_null() -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func is_not_null() -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func is_equal(_expected: Variant) -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func is_not_equal(_expected: Variant) -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func override_failure_message(message: String) -> GdUnitGodotErrorAssert: + _custom_failure_message = message + return self + + +func append_failure_message(message: String) -> GdUnitGodotErrorAssert: + _additional_failure_message = message + return self + + func is_success() -> GdUnitGodotErrorAssert: var log_entries := await _execute() if log_entries.is_empty(): @@ -74,7 +99,10 @@ func is_success() -> GdUnitGodotErrorAssert: """.dedent().trim_prefix("\n") % _to_list(log_entries)) -func is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert: +func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert: + var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error) + if result.is_error(): + return _report_error(result.error_message()) var log_entries := await _execute() if _has_log_entry(log_entries, ErrorLogEntry.TYPE.SCRIPT_ERROR, expected_error): return _report_success() @@ -85,7 +113,10 @@ func is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert: """.dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)]) -func is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert: +func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert: + var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_warning) + if result.is_error(): + return _report_error(result.error_message()) var log_entries := await _execute() if _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_WARNING, expected_warning): return _report_success() @@ -96,7 +127,10 @@ func is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert: """.dedent().trim_prefix("\n") % [expected_warning, _to_list(log_entries)]) -func is_push_error(expected_error :String) -> GdUnitGodotErrorAssert: +func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert: + var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error) + if result.is_error(): + return _report_error(result.error_message()) var log_entries := await _execute() if _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_ERROR, expected_error): return _report_success() diff --git a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid index 045e0503..f1b39ab4 100644 --- a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid @@ -1 +1 @@ -uid://dnw7cp2l6pll +uid://58p2kpktpckk diff --git a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd index 1527cac5..bdee249e 100644 --- a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd @@ -24,11 +24,13 @@ func current_value() -> Variant: func report_success() -> GdUnitIntAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitIntAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -37,13 +39,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitIntAssert: +func override_failure_message(message: String) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitIntAssert: +func append_failure_message(message: String) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,13 +63,13 @@ func is_not_null() -> GdUnitIntAssert: return self -func is_equal(expected :Variant) -> GdUnitIntAssert: +func is_equal(expected: Variant) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.is_equal(expected) return self -func is_not_equal(expected :Variant) -> GdUnitIntAssert: +func is_not_equal(expected: Variant) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.is_not_equal(expected) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid index fe35d23f..81e2b24a 100644 --- a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid @@ -1 +1 @@ -uid://crjqici2etcn +uid://borr8bsx6ha20 diff --git a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd index e6c5289e..955e1ef3 100644 --- a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd @@ -28,11 +28,13 @@ func current_value() -> Variant: func report_success() -> GdUnitObjectAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error: String) -> GdUnitObjectAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid index 4cbfc9b6..bc85519a 100644 --- a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid @@ -1 +1 @@ -uid://imlx5y72eoxp +uid://hnmpfnv31hco diff --git a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd index 8b6c7f26..98a6768f 100644 --- a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd @@ -28,11 +28,13 @@ func current_value() -> GdUnitResult: func report_success() -> GdUnitResultAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitResultAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -41,13 +43,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitResultAssert: +func override_failure_message(message: String) -> GdUnitResultAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitResultAssert: +func append_failure_message(message: String) -> GdUnitResultAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -65,6 +67,18 @@ func is_not_null() -> GdUnitResultAssert: return self +func is_equal(expected: Variant) -> GdUnitResultAssert: + return is_value(expected) + + +func is_not_equal(expected: Variant) -> GdUnitResultAssert: + var result := current_value() + var value :Variant = null if result == null else result.value() + if GdObjects.equals(value, expected): + return report_error(GdAssertMessages.error_not_equal(value, expected)) + return report_success() + + func is_empty() -> GdUnitResultAssert: var result := current_value() if result == null or not result.is_empty(): @@ -106,13 +120,9 @@ func contains_message(expected :String) -> GdUnitResultAssert: return report_success() -func is_value(expected :Variant) -> GdUnitResultAssert: +func is_value(expected: Variant) -> GdUnitResultAssert: var result := current_value() var value :Variant = null if result == null else result.value() if not GdObjects.equals(value, expected): return report_error(GdAssertMessages.error_result_is_value(value, expected)) return report_success() - - -func is_equal(expected :Variant) -> GdUnitResultAssert: - return is_value(expected) diff --git a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid index e3ab072e..719f6f76 100644 --- a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid @@ -1 +1 @@ -uid://de81t7hcub8oe +uid://b8247honhnsk4 diff --git a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd index 7d4f57e4..6f5878c8 100644 --- a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd @@ -50,12 +50,12 @@ func failure_message() -> String: return _current_failure_message -func override_failure_message(message :String) -> GdUnitSignalAssert: +func override_failure_message(message: String) -> GdUnitSignalAssert: _custom_failure_message = message return self -func append_failure_message(message :String) -> GdUnitSignalAssert: +func append_failure_message(message: String) -> GdUnitSignalAssert: _additional_failure_message = message return self @@ -70,6 +70,26 @@ func wait_until(timeout := 2000) -> GdUnitSignalAssert: return self +func is_null() -> GdUnitSignalAssert: + if _emitter != null: + return report_error(GdAssertMessages.error_is_null(_emitter)) + return report_success() + + +func is_not_null() -> GdUnitSignalAssert: + if _emitter == null: + return report_error(GdAssertMessages.error_is_not_null()) + return report_success() + + +func is_equal(_expected: Variant) -> GdUnitSignalAssert: + return report_error("Not implemented") + + +func is_not_equal(_expected: Variant) -> GdUnitSignalAssert: + return report_error("Not implemented") + + # Verifies the signal exists checked the emitter func is_signal_exists(signal_name :String) -> GdUnitSignalAssert: if not _emitter.has_signal(signal_name): diff --git a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid index a30fcbed..addb1c20 100644 --- a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid @@ -1 +1 @@ -uid://k22jnmpkjaat +uid://b2ckyxfrenygd diff --git a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd index 0f15956c..cb49c9ce 100644 --- a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd @@ -28,22 +28,24 @@ func current_value() -> Variant: func report_success() -> GdUnitStringAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitStringAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self -func override_failure_message(message :String) -> GdUnitStringAssert: +func override_failure_message(message: String) -> GdUnitStringAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitStringAssert: +func append_failure_message(message: String) -> GdUnitStringAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,8 +63,8 @@ func is_not_null() -> GdUnitStringAssert: return self -func is_equal(expected :Variant) -> GdUnitStringAssert: - var current :Variant = current_value() +func is_equal(expected: Variant) -> GdUnitStringAssert: + var current: Variant = current_value() if current == null: return report_error(GdAssertMessages.error_equal(current, expected)) if not GdObjects.equals(current, expected): @@ -83,8 +85,8 @@ func is_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert: return report_success() -func is_not_equal(expected :Variant) -> GdUnitStringAssert: - var current :Variant = current_value() +func is_not_equal(expected: Variant) -> GdUnitStringAssert: + var current: Variant = current_value() if GdObjects.equals(current, expected): return report_error(GdAssertMessages.error_not_equal(current, expected)) return report_success() diff --git a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid index 93cefa33..998a4749 100644 --- a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid @@ -1 +1 @@ -uid://bmtv6yn8kt4xe +uid://c3qm8guho3oli diff --git a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd index 7b10d6bb..fbc031a4 100644 --- a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd @@ -50,11 +50,13 @@ func current_value() -> Variant: func report_success() -> GdUnitVectorAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitVectorAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -63,7 +65,7 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitVectorAssert: +func override_failure_message(message: String) -> GdUnitVectorAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid index 81d20219..20696766 100644 --- a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid @@ -1 +1 @@ -uid://nqp5m5w32n4x +uid://cq07um0y41mgi diff --git a/addons/gdUnit4/src/asserts/ValueProvider.gd.uid b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid index 701c2504..fa9a9db7 100644 --- a/addons/gdUnit4/src/asserts/ValueProvider.gd.uid +++ b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid @@ -1 +1 @@ -uid://cev46jdufo3le +uid://cijud50eld7wd diff --git a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid index abdf283c..0b6a0b30 100644 --- a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid @@ -1 +1 @@ -uid://beemavaua76cw +uid://dp27dlfvuaqkb diff --git a/addons/gdUnit4/src/cmd/CmdCommand.gd.uid b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid index 696bb937..c50d3c29 100644 --- a/addons/gdUnit4/src/cmd/CmdCommand.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid @@ -1 +1 @@ -uid://xnrmy23u46ih +uid://dlj50tkefkksk diff --git a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid index d76a46e5..2347c8ef 100644 --- a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid @@ -1 +1 @@ -uid://c7qlmu0i3pnqe +uid://bc34u4nlpg2py diff --git a/addons/gdUnit4/src/cmd/CmdOption.gd.uid b/addons/gdUnit4/src/cmd/CmdOption.gd.uid index 24e77212..895cc3b6 100644 --- a/addons/gdUnit4/src/cmd/CmdOption.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdOption.gd.uid @@ -1 +1 @@ -uid://b8pylo1rq46rv +uid://0luks2etrlve diff --git a/addons/gdUnit4/src/cmd/CmdOptions.gd.uid b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid index f070c227..e9d64f19 100644 --- a/addons/gdUnit4/src/cmd/CmdOptions.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid @@ -1 +1 @@ -uid://cqsthu8atglvo +uid://dk816e4dekxq6 diff --git a/addons/gdUnit4/src/core/GdArrayTools.gd b/addons/gdUnit4/src/core/GdArrayTools.gd index 39b35b76..74f0e175 100644 --- a/addons/gdUnit4/src/core/GdArrayTools.gd +++ b/addons/gdUnit4/src/core/GdArrayTools.gd @@ -14,11 +14,12 @@ const ARRAY_TYPES := [ TYPE_PACKED_STRING_ARRAY, TYPE_PACKED_VECTOR2_ARRAY, TYPE_PACKED_VECTOR3_ARRAY, + TYPE_PACKED_VECTOR4_ARRAY, TYPE_PACKED_COLOR_ARRAY ] -static func is_array_type(value :Variant) -> bool: +static func is_array_type(value: Variant) -> bool: return is_type_array(typeof(value)) diff --git a/addons/gdUnit4/src/core/GdArrayTools.gd.uid b/addons/gdUnit4/src/core/GdArrayTools.gd.uid index 7a14d38a..b7bc89d8 100644 --- a/addons/gdUnit4/src/core/GdArrayTools.gd.uid +++ b/addons/gdUnit4/src/core/GdArrayTools.gd.uid @@ -1 +1 @@ -uid://ohwdj1xn4xsm +uid://byudd6vrw3ylx diff --git a/addons/gdUnit4/src/core/GdDiffTool.gd b/addons/gdUnit4/src/core/GdDiffTool.gd index 16e567b0..5131df71 100644 --- a/addons/gdUnit4/src/core/GdDiffTool.gd +++ b/addons/gdUnit4/src/core/GdDiffTool.gd @@ -1,4 +1,5 @@ -# A tool to find differences between two objects +# Myers' Diff Algorithm implementation +# Based on "An O(ND) Difference Algorithm and Its Variations" by Eugene W. Myers class_name GdDiffTool extends RefCounted @@ -7,79 +8,144 @@ const DIV_ADD :int = 214 const DIV_SUB :int = 215 -static func _diff(lb: PackedByteArray, rb: PackedByteArray, lookup: Array[Array], ldiff: Array, rdiff: Array) -> void: - var loffset := lb.size() - var roffset := rb.size() - - while true: - #if last character of X and Y matches - if loffset > 0 && roffset > 0 && lb[loffset - 1] == rb[roffset - 1]: - loffset -= 1 - roffset -= 1 - ldiff.push_front(lb[loffset]) - rdiff.push_front(rb[roffset]) - continue - #current character of Y is not present in X - else: if (roffset > 0 && (loffset == 0 || lookup[loffset][roffset - 1] >= lookup[loffset - 1][roffset])): - roffset -= 1 - ldiff.push_front(rb[roffset]) - ldiff.push_front(DIV_ADD) - rdiff.push_front(rb[roffset]) - rdiff.push_front(DIV_SUB) - continue - #current character of X is not present in Y - else: if (loffset > 0 && (roffset == 0 || lookup[loffset][roffset - 1] < lookup[loffset - 1][roffset])): - loffset -= 1 - ldiff.push_front(lb[loffset]) - ldiff.push_front(DIV_SUB) - rdiff.push_front(lb[loffset]) - rdiff.push_front(DIV_ADD) - continue - break - - -# lookup[i][j] stores the length of LCS of substring X[0..i-1], Y[0..j-1] -static func _createLookUp(lb: PackedByteArray, rb: PackedByteArray) -> Array[Array]: - var lookup: Array[Array] = [] - @warning_ignore("return_value_discarded") - lookup.resize(lb.size() + 1) - for i in lookup.size(): - var x := [] - @warning_ignore("return_value_discarded") - x.resize(rb.size() + 1) - lookup[i] = x - return lookup - - -static func _buildLookup(lb: PackedByteArray, rb: PackedByteArray) -> Array[Array]: - var lookup := _createLookUp(lb, rb) - # first column of the lookup table will be all 0 - for i in lookup.size(): - lookup[i][0] = 0 - # first row of the lookup table will be all 0 - for j :int in lookup[0].size(): - lookup[0][j] = 0 - - # fill the lookup table in bottom-up manner - for i in range(1, lookup.size()): - for j in range(1, lookup[0].size()): - # if current character of left and right matches - if lb[i - 1] == rb[j - 1]: - lookup[i][j] = lookup[i - 1][j - 1] + 1; - # else if current character of left and right don't match +class Edit: + enum Type { EQUAL, INSERT, DELETE } + var type: Type + var character: int + + func _init(t: Type, chr: int) -> void: + type = t + character = chr + + +# Main entry point - returns [ldiff, rdiff] +static func string_diff(left: Variant, right: Variant) -> Array[PackedInt32Array]: + var lb := PackedInt32Array() if left == null else str(left).to_utf32_buffer().to_int32_array() + var rb := PackedInt32Array() if right == null else str(right).to_utf32_buffer().to_int32_array() + + # Early exit for identical strings + if lb == rb: + return [lb.duplicate(), rb.duplicate()] + + var edits := _myers_diff(lb, rb) + return _edits_to_diff_format(edits) + + +# Core Myers' algorithm +static func _myers_diff(a: PackedInt32Array, b: PackedInt32Array) -> Array[Edit]: + var n := a.size() + var m := b.size() + var max_d := n + m + + # V array stores the furthest reaching x coordinate for each k-line + # We need indices from -max_d to max_d, so we offset by max_d + var v := PackedInt32Array() + v.resize(2 * max_d + 1) + v.fill(-1) + v[max_d + 1] = 0 # k=1 starts at x=0 + + var trace := [] # Store V arrays for each d to backtrack later + + # Find the edit distance + for d in range(0, max_d + 1): + # Store current V for backtracking + trace.append(v.duplicate()) + + for k in range(-d, d + 1, 2): + var k_offset := k + max_d + + # Decide whether to move down or right + var x: int + if k == -d or (k != d and v[k_offset - 1] < v[k_offset + 1]): + x = v[k_offset + 1] # Move down (insert from b) + else: + x = v[k_offset - 1] + 1 # Move right (delete from a) + + var y := x - k + + # Follow diagonal as far as possible (matching characters) + while x < n and y < m and a[x] == b[y]: + x += 1 + y += 1 + + v[k_offset] = x + + # Check if we've reached the end + if x >= n and y >= m: + return _backtrack(a, b, trace, d, max_d) + + # Should never reach here for valid inputs + return [] + + +# Backtrack through the edit graph to build the edit script +static func _backtrack(a: PackedInt32Array, b: PackedInt32Array, trace: Array, d: int, max_d: int) -> Array[Edit]: + var edits: Array[Edit] = [] + var x := a.size() + var y := b.size() + + # Walk backwards through each d value + for depth in range(d, -1, -1): + var v: PackedInt32Array = trace[depth] + var k := x - y + var k_offset := k + max_d + + # Determine previous k + var prev_k: int + if k == -depth or (k != depth and v[k_offset - 1] < v[k_offset + 1]): + prev_k = k + 1 + else: + prev_k = k - 1 + + var prev_k_offset := prev_k + max_d + var prev_x := v[prev_k_offset] + var prev_y := prev_x - prev_k + + # Extract diagonal (equal) characters + while x > prev_x and y > prev_y: + x -= 1 + y -= 1 + #var char_array := PackedInt32Array([a[x]]) + edits.insert(0, Edit.new(Edit.Type.EQUAL, a[x])) + + # Record the edit operation + if depth > 0: + if x == prev_x: + # Insert from b + y -= 1 + #var char_array := PackedInt32Array([b[y]]) + edits.insert(0, Edit.new(Edit.Type.INSERT, b[y])) else: - lookup[i][j] = max(lookup[i - 1][j], lookup[i][j - 1]); - return lookup - - -static func string_diff(left :Variant, right :Variant) -> Array[PackedByteArray]: - var lb := PackedByteArray() if left == null else str(left).to_utf8_buffer() - var rb := PackedByteArray() if right == null else str(right).to_utf8_buffer() - var ldiff := Array() - var rdiff := Array() - var lookup := _buildLookup(lb, rb); - _diff(lb, rb, lookup, ldiff, rdiff) - return [PackedByteArray(ldiff), PackedByteArray(rdiff)] + # Delete from a + x -= 1 + #var char_array := PackedInt32Array([a[x]]) + edits.insert(0, Edit.new(Edit.Type.DELETE, a[x])) + + return edits + + +# Convert edit script to the DIV_ADD/DIV_SUB format +static func _edits_to_diff_format(edits: Array[Edit]) -> Array[PackedInt32Array]: + var ldiff := PackedInt32Array() + var rdiff := PackedInt32Array() + + for edit in edits: + match edit.type: + Edit.Type.EQUAL: + ldiff.append(edit.character) + rdiff.append(edit.character) + Edit.Type.INSERT: + ldiff.append(DIV_ADD) + ldiff.append(edit.character) + rdiff.append(DIV_SUB) + rdiff.append(edit.character) + Edit.Type.DELETE: + ldiff.append(DIV_SUB) + ldiff.append(edit.character) + rdiff.append(DIV_ADD) + rdiff.append(edit.character) + + return [ldiff, rdiff] # prototype diff --git a/addons/gdUnit4/src/core/GdDiffTool.gd.uid b/addons/gdUnit4/src/core/GdDiffTool.gd.uid index 876b0a4c..01db2dc4 100644 --- a/addons/gdUnit4/src/core/GdDiffTool.gd.uid +++ b/addons/gdUnit4/src/core/GdDiffTool.gd.uid @@ -1 +1 @@ -uid://c7mdyylwmoa15 +uid://cenhwcwchexc diff --git a/addons/gdUnit4/src/core/GdObjects.gd b/addons/gdUnit4/src/core/GdObjects.gd index 358c21a8..4fec24e3 100644 --- a/addons/gdUnit4/src/core/GdObjects.gd +++ b/addons/gdUnit4/src/core/GdObjects.gd @@ -6,8 +6,6 @@ const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") # introduced with Godot 4.3.beta1 -const TYPE_PACKED_VECTOR4_ARRAY = 38 #TYPE_PACKED_VECTOR4_ARRAY - const TYPE_VOID = 1000 const TYPE_VARARG = 1001 const TYPE_VARIANT = 1002 @@ -20,10 +18,6 @@ const TYPE_CANVAS = 2003 const TYPE_ENUM = 2004 -# used as default value for varargs -const TYPE_VARARG_PLACEHOLDER_VALUE = "__null__" - - const TYPE_AS_STRING_MAPPINGS := { TYPE_NIL: "null", TYPE_BOOL: "bool", diff --git a/addons/gdUnit4/src/core/GdObjects.gd.uid b/addons/gdUnit4/src/core/GdObjects.gd.uid index a950b9f3..9bb25645 100644 --- a/addons/gdUnit4/src/core/GdObjects.gd.uid +++ b/addons/gdUnit4/src/core/GdObjects.gd.uid @@ -1 +1 @@ -uid://6tpr028xl5xm +uid://xu4wtc8ft2oe diff --git a/addons/gdUnit4/src/core/GdUnit4Version.gd.uid b/addons/gdUnit4/src/core/GdUnit4Version.gd.uid index 7b399cba..aad4e6d0 100644 --- a/addons/gdUnit4/src/core/GdUnit4Version.gd.uid +++ b/addons/gdUnit4/src/core/GdUnit4Version.gd.uid @@ -1 +1 @@ -uid://c78pbgijk03um +uid://bhvru0j8rks8g diff --git a/addons/gdUnit4/src/core/GdUnitFileAccess.gd b/addons/gdUnit4/src/core/GdUnitFileAccess.gd index cf8663c0..f6db5b4a 100644 --- a/addons/gdUnit4/src/core/GdUnitFileAccess.gd +++ b/addons/gdUnit4/src/core/GdUnitFileAccess.gd @@ -92,6 +92,7 @@ static func copy_directory(from_dir :String, to_dir :String, recursive :bool = f static func delete_directory(path :String, only_content := false) -> void: var dir := DirAccess.open(path) if dir != null: + dir.include_hidden = true @warning_ignore("return_value_discarded") dir.list_dir_begin() var file_name := "." @@ -154,11 +155,18 @@ static func find_last_path_index(path :String, prefix :String) -> int: return last_iteration +static func as_resource_path(value: String) -> String: + if value.begins_with("res://"): + return value + return "res://" + value.trim_prefix("//").trim_prefix("/").trim_suffix("/") + + static func scan_dir(path :String) -> PackedStringArray: var dir := DirAccess.open(path) if dir == null or not dir.dir_exists(path): return PackedStringArray() var content := PackedStringArray() + dir.include_hidden = true @warning_ignore("return_value_discarded") dir.list_dir_begin() var next := "." diff --git a/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid b/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid index 4a2e7de1..003b6c25 100644 --- a/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid @@ -1 +1 @@ -uid://br3lixulcwog6 +uid://gbc6op7on2fs diff --git a/addons/gdUnit4/src/core/GdUnitProperty.gd b/addons/gdUnit4/src/core/GdUnitProperty.gd index 48de036b..3d050b19 100644 --- a/addons/gdUnit4/src/core/GdUnitProperty.gd +++ b/addons/gdUnit4/src/core/GdUnitProperty.gd @@ -46,16 +46,18 @@ func is_selectable_value() -> bool: return not _value_set.is_empty() -func set_value(p_value :Variant) -> void: +func set_value(p_value: Variant) -> void: match _type: TYPE_STRING: _value = str(p_value) TYPE_BOOL: - _value = convert(p_value, TYPE_BOOL) + _value = type_convert(p_value, TYPE_BOOL) TYPE_INT: - _value = convert(p_value, TYPE_INT) + _value = type_convert(p_value, TYPE_INT) TYPE_FLOAT: - _value = convert(p_value, TYPE_FLOAT) + _value = type_convert(p_value, TYPE_FLOAT) + TYPE_DICTIONARY: + _value = type_convert(p_value, TYPE_DICTIONARY) _: _value = p_value diff --git a/addons/gdUnit4/src/core/GdUnitProperty.gd.uid b/addons/gdUnit4/src/core/GdUnitProperty.gd.uid index aa95e345..ed85b571 100644 --- a/addons/gdUnit4/src/core/GdUnitProperty.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitProperty.gd.uid @@ -1 +1 @@ -uid://2ud3bs2h3yve +uid://buvw2dlcb3yc4 diff --git a/addons/gdUnit4/src/core/GdUnitResult.gd b/addons/gdUnit4/src/core/GdUnitResult.gd index c7187d48..42392a5e 100644 --- a/addons/gdUnit4/src/core/GdUnitResult.gd +++ b/addons/gdUnit4/src/core/GdUnitResult.gd @@ -20,7 +20,7 @@ static func empty() -> GdUnitResult: return result -static func success(p_value :Variant) -> GdUnitResult: +static func success(p_value: Variant = "") -> GdUnitResult: assert(p_value != null, "The value must not be NULL") var result := GdUnitResult.new() result._value = p_value @@ -28,7 +28,7 @@ static func success(p_value :Variant) -> GdUnitResult: return result -static func warn(p_warn_message :String, p_value :Variant = null) -> GdUnitResult: +static func warn(p_warn_message: String, p_value: Variant = null) -> GdUnitResult: assert(not p_warn_message.is_empty()) #,"The message must not be empty") var result := GdUnitResult.new() result._value = p_value @@ -37,7 +37,7 @@ static func warn(p_warn_message :String, p_value :Variant = null) -> GdUnitResul return result -static func error(p_error_message :String) -> GdUnitResult: +static func error(p_error_message: String) -> GdUnitResult: assert(not p_error_message.is_empty(), "The message must not be empty") var result := GdUnitResult.new() result._value = null @@ -70,7 +70,7 @@ func value_as_string() -> String: return _value -func or_else(p_value :Variant) -> Variant: +func or_else(p_value: Variant) -> Variant: if not is_success(): return p_value return value() @@ -88,7 +88,7 @@ func _to_string() -> String: return str(GdUnitResult.serialize(self)) -static func serialize(result :GdUnitResult) -> Dictionary: +static func serialize(result: GdUnitResult) -> Dictionary: if result == null: push_error("Can't serialize a Null object from type GdUnitResult") return { @@ -99,7 +99,7 @@ static func serialize(result :GdUnitResult) -> Dictionary: } -static func deserialize(config :Dictionary) -> GdUnitResult: +static func deserialize(config: Dictionary) -> GdUnitResult: var result := GdUnitResult.new() var cfg_value: String = config.get("value", "") result._value = str_to_var(cfg_value) diff --git a/addons/gdUnit4/src/core/GdUnitResult.gd.uid b/addons/gdUnit4/src/core/GdUnitResult.gd.uid index 0a9bb31a..306b8318 100644 --- a/addons/gdUnit4/src/core/GdUnitResult.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitResult.gd.uid @@ -1 +1 @@ -uid://18326kauqx8a +uid://d0qa3g2v46ctk diff --git a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid index cd0c96e4..320abda6 100644 --- a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid @@ -1 +1 @@ -uid://2h8ayeuwtr3b +uid://drb62k7voxhuv diff --git a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd index b8f1c82d..f2718537 100644 --- a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd +++ b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd @@ -82,7 +82,8 @@ func _init(p_scene: Variant, p_verbose: bool, p_hide_push_errors := false) -> vo ) _simulate_start_time = LocalTime.now() # we need to set inital a valid window otherwise the warp_mouse() is not handled - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + move_window_to_foreground() + # set inital mouse pos to 0,0 var max_iteration_to_wait := 0 while get_global_mouse_position() != Vector2.ZERO and max_iteration_to_wait < 100: @@ -95,6 +96,7 @@ func _notification(what: int) -> void: # reset time factor to normal __deactivate_time_factor() if is_instance_valid(_current_scene): + move_window_to_background() _scene_tree().root.remove_child(_current_scene) # do only free scenes instanciated by this runner if _scene_auto_free: @@ -107,33 +109,36 @@ func _scene_tree() -> SceneTree: return Engine.get_main_loop() as SceneTree +func await_input_processed() -> void: + if scene() != null and scene().process_mode != Node.PROCESS_MODE_DISABLED: + Input.flush_buffered_events() + await (Engine.get_main_loop() as SceneTree).process_frame + await (Engine.get_main_loop() as SceneTree).physics_frame + + @warning_ignore("return_value_discarded") -func simulate_action_pressed(action: String) -> GdUnitSceneRunner: - simulate_action_press(action) - simulate_action_release(action) +func simulate_action_pressed(action: String, event_index := -1) -> GdUnitSceneRunner: + simulate_action_press(action, event_index) + simulate_action_release(action, event_index) return self -func simulate_action_press(action: String) -> GdUnitSceneRunner: +func simulate_action_press(action: String, event_index := -1) -> GdUnitSceneRunner: __print_current_focus() var event := InputEventAction.new() event.pressed = true event.action = action - if Engine.get_version_info().hex >= 0x40300: - @warning_ignore("unsafe_property_access") - event.event_index = InputMap.get_actions().find(action) + event.event_index = event_index _action_on_press.append(action) return _handle_input_event(event) -func simulate_action_release(action: String) -> GdUnitSceneRunner: +func simulate_action_release(action: String, event_index := -1) -> GdUnitSceneRunner: __print_current_focus() var event := InputEventAction.new() event.pressed = false event.action = action - if Engine.get_version_info().hex >= 0x40300: - @warning_ignore("unsafe_property_access") - event.event_index = InputMap.get_actions().find(action) + event.event_index = event_index _action_on_press.erase(action) return _handle_input_event(event) @@ -152,6 +157,7 @@ func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := f event.pressed = true event.keycode = key_code as Key event.physical_keycode = key_code as Key + event.unicode = key_code event.alt_pressed = key_code == KEY_ALT event.shift_pressed = shift_pressed or key_code == KEY_SHIFT event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL @@ -166,6 +172,7 @@ func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := event.pressed = false event.keycode = key_code as Key event.physical_keycode = key_code as Key + event.unicode = key_code event.alt_pressed = key_code == KEY_ALT event.shift_pressed = shift_pressed or key_code == KEY_SHIFT event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL @@ -174,10 +181,6 @@ func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := return _handle_input_event(event) -func set_mouse_pos(pos: Vector2) -> GdUnitSceneRunner: - return set_mouse_position(pos) - - func set_mouse_position(pos: Vector2) -> GdUnitSceneRunner: var event := InputEventMouseMotion.new() event.position = pos @@ -277,7 +280,7 @@ func simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := func simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner: if is_emulate_mouse_from_touch(): # we need to simulate in addition to the touch the mouse events - set_mouse_pos(position) + set_mouse_position(position) simulate_mouse_button_press(MOUSE_BUTTON_LEFT) # push touch press event at position var event := InputEventScreenTouch.new() @@ -413,46 +416,21 @@ func simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner: return self -func simulate_until_signal( - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG) +func simulate_until_signal(signal_name: String, ...args: Array) -> GdUnitSceneRunner: await _awaiter.await_signal_idle_frames(scene(), signal_name, args, 10000) return self -func simulate_until_object_signal( - source: Object, - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG) +func simulate_until_object_signal(source: Object, signal_name: String, ...args: Array) -> GdUnitSceneRunner: await _awaiter.await_signal_idle_frames(source, signal_name, args, 10000) return self -func await_func(func_name: String, args := []) -> GdUnitFuncAssert: +func await_func(func_name: String, ...args: Array) -> GdUnitFuncAssert: return GdUnitFuncAssertImpl.new(scene(), func_name, args) -func await_func_on(instance: Object, func_name: String, args := []) -> GdUnitFuncAssert: +func await_func_on(instance: Object, func_name: String, ...args: Array) -> GdUnitFuncAssert: return GdUnitFuncAssertImpl.new(instance, func_name, args) @@ -464,9 +442,17 @@ func await_signal_on(source: Object, signal_name: String, args := [], timeout := await _awaiter.await_signal_on(source, signal_name, args, timeout) -func maximize_view() -> GdUnitSceneRunner: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) - DisplayServer.window_move_to_foreground() +func move_window_to_foreground() -> GdUnitSceneRunner: + if not Engine.is_embedded_in_editor(): + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + DisplayServer.window_move_to_foreground() + return self + + +func move_window_to_background() -> GdUnitSceneRunner: + if not Engine.is_embedded_in_editor(): + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) return self @@ -488,19 +474,7 @@ func set_property(name: String, value: Variant) -> bool: return true -func invoke( - name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> Variant: - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG) +func invoke(name: String, ...args: Array) -> Variant: if scene().has_method(name): return await scene().callv(name, args) return "The method '%s' not exist checked loaded scene." % name diff --git a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid index 16951a06..9ba20e8b 100644 --- a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid @@ -1 +1 @@ -uid://bie8y6aewygkc +uid://chd16s5ispqeq diff --git a/addons/gdUnit4/src/core/GdUnitSettings.gd b/addons/gdUnit4/src/core/GdUnitSettings.gd index 2fc1cff2..f6bff127 100644 --- a/addons/gdUnit4/src/core/GdUnitSettings.gd +++ b/addons/gdUnit4/src/core/GdUnitSettings.gd @@ -11,6 +11,9 @@ const GROUP_COMMON = COMMON_SETTINGS + "/common" const UPDATE_NOTIFICATION_ENABLED = GROUP_COMMON + "/update_notification_enabled" const SERVER_TIMEOUT = GROUP_COMMON + "/server_connection_timeout_minutes" +const GROUP_HOOKS = MAIN_CATEGORY + "/hooks" +const SESSION_HOOKS = GROUP_HOOKS + "/session_hooks" + const GROUP_TEST = COMMON_SETTINGS + "/test" const TEST_TIMEOUT = GROUP_TEST + "/test_timeout_seconds" const TEST_LOOKUP_FOLDER = GROUP_TEST + "/test_lookup_folder" @@ -74,6 +77,10 @@ const SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG = GROUP_SHORTCUT_FILESYSTEM + "/run_tes const GROUP_UI_TOOLBAR = UI_SETTINGS + "/toolbar" const INSPECTOR_TOOLBAR_BUTTON_RUN_OVERALL = GROUP_UI_TOOLBAR + "/run_overall" +# Feature flags +const GROUP_FEATURE = MAIN_CATEGORY + "/feature" + + # defaults # server connection timeout in minutes const DEFAULT_SERVER_TIMEOUT :int = 30 @@ -123,6 +130,7 @@ static func setup() -> void: "Show 'Run overall Tests' button in the inspector toolbar") create_property_if_need(TEMPLATE_TS_GD, GdUnitTestSuiteTemplate.default_GD_template(), "Test suite template to use") create_shortcut_properties_if_need() + create_property_if_need(SESSION_HOOKS, {} as Dictionary[String,bool]) migrate_properties() @@ -199,6 +207,20 @@ static func set_log_path(path :String) -> void: ProjectSettings.save() +static func get_session_hooks() -> Dictionary[String, bool]: + var property := get_property(SESSION_HOOKS) + if property == null: + return {} + var hooks: Dictionary[String, bool] = property.value() + return hooks + + +static func set_session_hooks(hooks: Dictionary[String, bool]) -> void: + var property := get_property(SESSION_HOOKS) + property.set_value(hooks) + update_property(property) + + static func set_inspector_tree_sort_mode(sort_mode: GdUnitInspectorTreeConstants.SORT_MODE) -> void: var property := get_property(INSPECTOR_TREE_SORT_MODE) property.set_value(sort_mode) @@ -276,6 +298,10 @@ static func is_test_flaky_check_enabled() -> bool: return get_setting(TEST_FLAKY_CHECK, false) +static func is_feature_enabled(feature: String) -> bool: + return get_setting(feature, false) + + static func get_flaky_max_retries() -> int: return get_setting(TEST_FLAKY_MAX_RETRIES, 3) diff --git a/addons/gdUnit4/src/core/GdUnitSettings.gd.uid b/addons/gdUnit4/src/core/GdUnitSettings.gd.uid index 57828e51..4b0395b4 100644 --- a/addons/gdUnit4/src/core/GdUnitSettings.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSettings.gd.uid @@ -1 +1 @@ -uid://cbm8sw2x2se0q +uid://e6v62hm1dmcw diff --git a/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid b/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid index 2c8126ad..508ef89e 100644 --- a/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid @@ -1 +1 @@ -uid://y0rn2oyirlex +uid://414emodsaytw diff --git a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd index 10bacc4a..d03cc8ea 100644 --- a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd +++ b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd @@ -82,17 +82,22 @@ func _on_signal_emmited( (_collected_signals[emitter][signal_name] as Array).append(signal_args) -func reset_received_signals(emitter :Object, signal_name: String, signal_args :Array) -> void: +func reset_received_signals(emitter: Object, signal_name: String, signal_args: Array) -> void: #_debug_signal_list("before claer"); if _collected_signals.has(emitter): var signals_by_emitter :Dictionary = _collected_signals[emitter] if signals_by_emitter.has(signal_name): - @warning_ignore("unsafe_cast") - (_collected_signals[emitter][signal_name] as Array).erase(signal_args) + var received_args: Array = _collected_signals[emitter][signal_name] + # We iterate backwarts over to received_args to remove matching args. + # This will avoid array corruption see comment on `erase` otherwise we need a timeconsuming duplicate before + for arg_pos: int in range(received_args.size()-1, -1, -1): + var arg: Variant = received_args[arg_pos] + if GdObjects.equals(arg, signal_args): + received_args.remove_at(arg_pos) #_debug_signal_list("after claer"); -func is_signal_collecting(emitter :Object, signal_name :String) -> bool: +func is_signal_collecting(emitter: Object, signal_name: String) -> bool: @warning_ignore("unsafe_cast") return _collected_signals.has(emitter) and (_collected_signals[emitter] as Dictionary).has(signal_name) diff --git a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid index 3af33bf5..457f78e4 100644 --- a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid @@ -1 +1 @@ -uid://dvy6du6ff1isn +uid://doug8b4ss50js diff --git a/addons/gdUnit4/src/core/GdUnitSignals.gd.uid b/addons/gdUnit4/src/core/GdUnitSignals.gd.uid index d7e740e5..274f2895 100644 --- a/addons/gdUnit4/src/core/GdUnitSignals.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSignals.gd.uid @@ -1 +1 @@ -uid://bkao3mr1pn63l +uid://ymfnyi5ktfvi diff --git a/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid b/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid index 36b4ca93..84dfa10c 100644 --- a/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid @@ -1 +1 @@ -uid://ey3ar3bnx130 +uid://cr1tgrxl00b4w diff --git a/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid b/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid index 409a60c6..29bd5600 100644 --- a/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid @@ -1 +1 @@ -uid://bfwul6jc4klx +uid://dqyv0ij2phkuc diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid b/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid index 8f81c17b..205a7cb7 100644 --- a/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid @@ -1 +1 @@ -uid://nxwwbijsu4o +uid://r644f6fjvox5 diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd index 654bdc88..cc4dbffb 100644 --- a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd +++ b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd @@ -168,7 +168,7 @@ func _build_test_attribute(script: GDScript, fd: GdFunctionDescriptor) -> TestCa else: match arg.name(): ARGUMENT_TIMEOUT: - attribute.timeout = convert(arg.default(), TYPE_INT) + attribute.timeout = type_convert(arg.default(), TYPE_INT) ARGUMENT_SKIP: var result: Variant = _expression_runner.execute(script, arg.plain_value()) if result is bool: @@ -178,9 +178,9 @@ func _build_test_attribute(script: GDScript, fd: GdFunctionDescriptor) -> TestCa ARGUMENT_SKIP_REASON: attribute.skip_reason = arg.plain_value() Fuzzer.ARGUMENT_ITERATIONS: - attribute.fuzzer_iterations = convert(arg.default(), TYPE_INT) + attribute.fuzzer_iterations = type_convert(arg.default(), TYPE_INT) Fuzzer.ARGUMENT_SEED: - attribute.test_seed = convert(arg.default(), TYPE_INT) + attribute.test_seed = type_convert(arg.default(), TYPE_INT) ARGUMENT_PARAMETER_SET: collected_unknown_aruments.clear() pass diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid index f3c3da14..27b43b2a 100644 --- a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid @@ -1 +1 @@ -uid://cgr6xvkbvrvbd +uid://c4sgyh4pxh7wa diff --git a/addons/gdUnit4/src/core/GdUnitTools.gd.uid b/addons/gdUnit4/src/core/GdUnitTools.gd.uid index cb5c9feb..48ffec7f 100644 --- a/addons/gdUnit4/src/core/GdUnitTools.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTools.gd.uid @@ -1 +1 @@ -uid://btxp1l2o5bpyc +uid://ddcnbxwxxons6 diff --git a/addons/gdUnit4/src/core/GodotVersionFixures.gd b/addons/gdUnit4/src/core/GodotVersionFixures.gd index 9d5b6bb3..52993251 100644 --- a/addons/gdUnit4/src/core/GodotVersionFixures.gd +++ b/addons/gdUnit4/src/core/GodotVersionFixures.gd @@ -3,16 +3,6 @@ class_name GodotVersionFixures extends RefCounted -@warning_ignore("shadowed_global_identifier") -static func type_convert(value: Variant, type: int) -> Variant: - return convert(value, type) - - -@warning_ignore("shadowed_global_identifier") -static func convert(value: Variant, type: int) -> Variant: - return type_convert(value, type) - - # handle global_position fixed by https://github.com/godotengine/godot/pull/88473 static func set_event_global_position(event: InputEventMouseMotion, global_position: Vector2) -> void: if Engine.get_version_info().hex >= 0x40202 or Engine.get_version_info().hex == 0x40104: diff --git a/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid b/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid index c44eee92..66806ce2 100644 --- a/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid +++ b/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid @@ -1 +1 @@ -uid://ca6k0ct4ps4wx +uid://de2xpk0iv75qc diff --git a/addons/gdUnit4/src/core/LocalTime.gd.uid b/addons/gdUnit4/src/core/LocalTime.gd.uid index dbe3fecb..2ff3b9ee 100644 --- a/addons/gdUnit4/src/core/LocalTime.gd.uid +++ b/addons/gdUnit4/src/core/LocalTime.gd.uid @@ -1 +1 @@ -uid://47hui0sp7viu +uid://dmvcxp00ggdum diff --git a/addons/gdUnit4/src/core/_TestCase.gd.uid b/addons/gdUnit4/src/core/_TestCase.gd.uid index ddcf74a7..3250d96b 100644 --- a/addons/gdUnit4/src/core/_TestCase.gd.uid +++ b/addons/gdUnit4/src/core/_TestCase.gd.uid @@ -1 +1 @@ -uid://btpl5hq0u4ycf +uid://db7crai0yefiy diff --git a/addons/gdUnit4/src/core/assets/touch-button.png.import b/addons/gdUnit4/src/core/assets/touch-button.png.import index a84ce13b..bab0a891 100644 --- a/addons/gdUnit4/src/core/assets/touch-button.png.import +++ b/addons/gdUnit4/src/core/assets/touch-button.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://btx5kcrsngasl" +uid="uid://b6hqcav1spm6w" path="res://.godot/imported/touch-button.png-2fff40c8520d8e97a57db1b2b043f641.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/touch-button.png-2fff40c8520d8e97a57db1b2b043 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid b/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid index 529de015..e7d74862 100644 --- a/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid +++ b/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid @@ -1 +1 @@ -uid://fhrrc4imyitw +uid://pcrhly3bmbqx diff --git a/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid index 8cbd0e25..09b6022f 100644 --- a/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid +++ b/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid @@ -1 +1 @@ -uid://qmmhjhsguag1 +uid://ifaj7wbj5t6u diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd index bf688df5..c8130cc1 100644 --- a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd +++ b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd @@ -245,7 +245,6 @@ func cmd_run(debug: bool) -> void: if _is_running: return - GdUnitSignals.instance().gdunit_event.emit(GdUnitInit.new()) # save current selected excution config var server_port: int = Engine.get_meta("gdunit_server_port") var result := _runner_config.set_server_port(server_port).save_config() @@ -383,7 +382,7 @@ func active_script() -> Script: # signals handles ################################################################################ func _on_event(event: GdUnitEvent) -> void: - if event.type() == GdUnitEvent.STOP: + if event.type() == GdUnitEvent.SESSION_CLOSE: cmd_stop(_client_id) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid index 84c78139..ec372357 100644 --- a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid +++ b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid @@ -1 +1 @@ -uid://c2pqwxvwtc0uc +uid://d3htpd81h8rej diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid index 6fa91e49..ecd7e115 100644 --- a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid +++ b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid @@ -1 +1 @@ -uid://bo05nreg5oook +uid://dlvygvdx4hbyb diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid b/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid index 57888b1c..54884e8e 100644 --- a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid +++ b/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid @@ -1 +1 @@ -uid://otd8xqcbw8rh +uid://cakgocix5diax diff --git a/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid index 9d92c84a..50c19bbf 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid @@ -1 +1 @@ -uid://ectm404g3m5n +uid://b1npkt8wb5uat diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid index 5611cd25..8ceb063b 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid @@ -1 +1 @@ -uid://dj4af2kummp71 +uid://dymwd1exlme2l diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd index 4f9111d0..6af8255a 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd @@ -143,6 +143,13 @@ func find_test_by_id(id: GdUnitGUID) -> GdUnitTestCase: return null +func get_discovered_tests() -> Array[GdUnitTestCase]: + var discovered_tests: Array[GdUnitTestCase] = [] + for test_sets: Array[GdUnitTestCase] in _discover_cache.values(): + discovered_tests.append_array(test_sets) + return discovered_tests + + ## Discovers tests in a script and tracks changes.[br] ## [br] ## Handles both GDScript and C# test suites.[br] diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid index ee0ba643..41cdc131 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid @@ -1 +1 @@ -uid://duq8grt8k2x5y +uid://b72tonik333el diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid index 44559a4f..5a9f7a23 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid @@ -1 +1 @@ -uid://csfcjnh3mijbf +uid://bacpebjcex3oo diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd index 996dd95c..cbe48589 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd @@ -36,7 +36,7 @@ static func run() -> Array[GdUnitTestCase]: console_log_discover_results(collected_tests) if !recovered_tests.is_empty(): - console_log("Recovery last test session successfully, %d tests restored." % recovered_tests.size(), true) + console_log("Recovered last test session successfully, %d tests restored." % recovered_tests.size(), true) return collected_tests ) # wait unblocked to the tread is finished @@ -72,12 +72,12 @@ static func restore_last_session() -> void: t.start(func () -> void: # Do sync the main thread before emit the discovered test suites to the inspector await (Engine.get_main_loop() as SceneTree).process_frame - console_log("Recovery last test session ..", true) + console_log("Recovering last test session ..", true) GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverStart.new()) for test_case in test_cases: GdUnitTestDiscoverSink.discover(test_case) GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverEnd.new(0, 0)) - console_log("Recovery last test session successfully, %d tests restored." % test_cases.size(), true) + console_log("Recovered last test session successfully, %d tests restored." % test_cases.size(), true) ) t.wait_to_finish() @@ -94,7 +94,7 @@ static func console_log_discover_results(tests: Array[GdUnitTestCase]) -> void: ) for suite_tests: Array in grouped_by_suites.values(): var test_case: GdUnitTestCase = suite_tests[0] - console_log("Discover: TestSuite %s with %d tests fount" % [test_case.source_file, suite_tests.size()]) + console_log("Discover: TestSuite %s with %d tests found" % [test_case.source_file, suite_tests.size()]) console_log("Discover tests done, %d TestSuites and total %d Tests found. " % [grouped_by_suites.size(), tests.size()]) console_log("") diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid index 87d82480..b430bcce 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid @@ -1 +1 @@ -uid://bln038pf0fmrx +uid://c7t8ko1do1oph diff --git a/addons/gdUnit4/src/core/event/GdUnitEvent.gd b/addons/gdUnit4/src/core/event/GdUnitEvent.gd index d6c191a7..6bec1059 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEvent.gd +++ b/addons/gdUnit4/src/core/event/GdUnitEvent.gd @@ -22,6 +22,8 @@ enum { TESTCASE_AFTER, DISCOVER_START, DISCOVER_END, + SESSION_START, + SESSION_CLOSE } var _event_type: int @@ -126,7 +128,7 @@ func resource_path() -> String: func is_success() -> bool: - return not is_warning() and not is_failed() and not is_error() and not is_skipped() + return not is_failed() and not is_error() func is_warning() -> bool: diff --git a/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid index 129afa9a..0808c4f1 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid @@ -1 +1 @@ -uid://y0nnypmdrlcn +uid://cguq5nst146xo diff --git a/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid index dd3552a6..5cf31acc 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid @@ -1 +1 @@ -uid://cx2fp7r60783w +uid://dj4u5s5jt1x4e diff --git a/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid index 6114cc49..724a4655 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid @@ -1 +1 @@ -uid://kj3plfhahenl +uid://d1vfy6bu2dsev diff --git a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid index 1d5ba862..fa3db66e 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid @@ -1 +1 @@ -uid://mx4li657l68s +uid://cioyas7ocjhkf diff --git a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid index 30787fe0..b5a20769 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid @@ -1 +1 @@ -uid://hc3k3cnv4xsy +uid://fjgjx7eed7hd diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd new file mode 100644 index 00000000..52dab3ff --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd @@ -0,0 +1,6 @@ +class_name GdUnitSessionClose +extends GdUnitEvent + + +func _init() -> void: + _event_type = SESSION_CLOSE diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd.uid b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd.uid new file mode 100644 index 00000000..4fe73a36 --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd.uid @@ -0,0 +1 @@ +uid://blbk7ntymec0t diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd new file mode 100644 index 00000000..420ad538 --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd @@ -0,0 +1,6 @@ +class_name GdUnitSessionStart +extends GdUnitEvent + + +func _init() -> void: + _event_type = SESSION_START diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd.uid b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd.uid new file mode 100644 index 00000000..9774f037 --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd.uid @@ -0,0 +1 @@ +uid://co8xoyftboegs diff --git a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd index 4f0e6025..457fd678 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd +++ b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd @@ -2,6 +2,14 @@ ## 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 @@ -14,20 +22,7 @@ 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() - - -# execution states -var _is_calculated := false -var _is_success: bool -var _is_flaky: bool -var _is_skipped: bool -var _has_warnings: bool -var _has_failures: bool -var _has_errors: bool -var _failure_count := 0 -var _orphan_count := 0 -var _error_count := 0 -var _skipped_count := 0 +var _orphans := -1 var error_monitor: GodotGdErrorMonitor = null: @@ -143,8 +138,9 @@ func orphan_monitor_stop() -> void: _orphan_monitor.stop() -func add_report(report: GdUnitReport) -> void: +func add_report(report: GdUnitReport) -> GdUnitReport: _report_collector.push_back(report) + return report func reports() -> Array[GdUnitReport]: @@ -154,144 +150,61 @@ func reports() -> Array[GdUnitReport]: func collect_reports(recursive: bool) -> Array[GdUnitReport]: if not recursive: return reports() - var current_reports := 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.reports()) - # needs finally to clean the test reports to avoid counting twice - sub_context.reports().clear() + current_reports.append_array(sub_context.collect_reports(true)) + return current_reports -func collect_orphans(p_reports: Array[GdUnitReport]) -> int: - var orphans := 0 - if not _sub_context.is_empty(): - orphans += collect_testcase_orphan_reports(_sub_context[0], p_reports) - orphans += collect_teststage_orphan_reports(p_reports) - return orphans - - -func collect_testcase_orphan_reports(context: GdUnitExecutionContext, p_reports: Array[GdUnitReport]) -> int: - var orphans := context.count_orphans() - if orphans > 0: - p_reports.push_front(GdUnitReport.new()\ - .create(GdUnitReport.WARN, context.test_case.line_number(), GdAssertMessages.orphan_detected_on_test(orphans))) - return orphans - - -func collect_teststage_orphan_reports(p_reports: Array[GdUnitReport]) -> int: - var orphans := count_orphans() - if orphans > 0: - p_reports.push_front(GdUnitReport.new()\ - .create(GdUnitReport.WARN, test_case.line_number(), GdAssertMessages.orphan_detected_on_test_setup(orphans))) - return orphans - - -func build_reports(recursive:= true) -> Array[GdUnitReport]: - var collected_reports: Array[GdUnitReport] = collect_reports(recursive) - if recursive: - _orphan_count = collect_orphans(collected_reports) - else: - _orphan_count = count_orphans() - if _orphan_count > 0: - collected_reports.push_front(GdUnitReport.new() \ - .create(GdUnitReport.WARN, 1, GdAssertMessages.orphan_detected_on_suite_setup(_orphan_count))) - _is_skipped = is_skipped() - _skipped_count = count_skipped(recursive) - _is_success = is_success() - _is_flaky = is_flaky() - _has_warnings = has_warnings() - _has_errors = has_errors() - _error_count = count_errors(recursive) - if !_is_success: - _has_failures = has_failures() - _failure_count = count_failures(recursive) - _is_calculated = true - return collected_reports - - -# Evaluates the actual test case status by validate latest execution state (cold be more based on flaky max retry count) -func evaluate_test_retry_status() -> bool: - # get latest test execution status - var last_test_status :GdUnitExecutionContext = _sub_context.back() - _is_skipped = last_test_status.is_skipped() - _skipped_count = last_test_status.count_skipped(false) - _is_success = last_test_status.is_success() - # if success but it have more than one sub contexts the test was rerurn becouse of failures and will be marked as flaky - _is_flaky = _is_success and _sub_context.size() > 1 - _has_warnings = last_test_status.has_warnings() - _has_errors = last_test_status.has_errors() - _error_count = last_test_status.count_errors(false) - _has_failures = last_test_status.has_failures() - _failure_count = last_test_status.count_failures(false) - _orphan_count = last_test_status.collect_orphans(collect_reports(false)) - _is_calculated = true - # finally cleanup the retry execution contexts - dispose_sub_contexts() - return _is_success +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 - -func get_execution_statistics() -> Dictionary: return { - GdUnitEvent.RETRY_COUNT: _test_execution_iteration, - GdUnitEvent.ORPHAN_NODES: _orphan_count, - GdUnitEvent.ELAPSED_TIME: _timer.elapsed_since_ms(), - GdUnitEvent.FAILED: !_is_success, - GdUnitEvent.ERRORS: _has_errors, - GdUnitEvent.WARNINGS: _has_warnings, - GdUnitEvent.FLAKY: _is_flaky, - GdUnitEvent.SKIPPED: _is_skipped, - GdUnitEvent.FAILED_COUNT: _failure_count, - GdUnitEvent.ERROR_COUNT: _error_count, - GdUnitEvent.SKIPPED_COUNT: _skipped_count + 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 has_failures() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._has_failures if c._is_calculated else c.has_failures()) - or _report_collector.has_failures() - ) - - -func has_errors() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._has_errors if c._is_calculated else c.has_errors()) - or _report_collector.has_errors() - ) - - -func has_warnings() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._has_warnings if c._is_calculated else c.has_warnings()) - or _report_collector.has_warnings() - ) - - -func is_flaky() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._is_flaky if c._is_calculated else c.is_flaky()) - or _test_execution_iteration > 1 - ) - - func is_success() -> bool: if _sub_context.is_empty(): - return not has_failures() + return not _report_collector.has_failures() + # we on test suite level? + if _parent_context == null: + return not _report_collector.has_failures() - var failed_context := _sub_context.filter(func(c :GdUnitExecutionContext) -> bool: - return !(c._is_success if c._is_calculated else c.is_success())) - return failed_context.is_empty() and not 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 if c._is_calculated else c.is_skipped()) + return c.is_skipped()) or test_case.is_skipped() if test_case != null else false ) @@ -300,35 +213,20 @@ func is_interupted() -> bool: return false if test_case == null else test_case.is_interupted() -func count_failures(recursive: bool) -> int: - if not recursive: - return _report_collector.count_failures() - return _sub_context\ - .map(func(c :GdUnitExecutionContext) -> int: - return c.count_failures(recursive)).reduce(sum, _report_collector.count_failures()) - - -func count_errors(recursive: bool) -> int: - if not recursive: - return _report_collector.count_errors() - return _sub_context\ - .map(func(c :GdUnitExecutionContext) -> int: - return c.count_errors(recursive)).reduce(sum, _report_collector.count_errors()) - - -func count_skipped(recursive: bool) -> int: - if not recursive: - return _report_collector.count_skipped() - return _sub_context\ - .map(func(c :GdUnitExecutionContext) -> int: - return c.count_skipped(recursive)).reduce(sum, _report_collector.count_skipped()) - +func _count_orphans() -> int: + if _orphans != -1: + return _orphans -func count_orphans() -> int: var orphans := 0 for c in _sub_context: - orphans += c._orphan_monitor.orphan_nodes() - return _orphan_monitor.orphan_nodes() - orphans + 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: @@ -336,7 +234,7 @@ func sum(accum: int, number: int) -> int: func retry_execution() -> bool: - var retry := _test_execution_iteration < 1 if not _flaky_test_check else _test_execution_iteration < _flaky_test_retries + 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 @@ -346,9 +244,26 @@ func register_auto_free(obj: Variant) -> Variant: return _memory_observer.register_auto_free(obj) -## Runs the gdunit garbage collector to free registered object -func gc() -> void: +## 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))) diff --git a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid index bd24a09f..da2caf06 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid @@ -1 +1 @@ -uid://d278b021rfktw +uid://cai4qajmg83ab diff --git a/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid index 67df8451..f82adf14 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid @@ -1 +1 @@ -uid://0l86bfc43kn2 +uid://cmpw40u4k3on6 diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd index 62c4b02f..5f42d148 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd +++ b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd @@ -22,20 +22,20 @@ static func __filter_is_skipped(report :GdUnitReport) -> bool: return report.is_skipped() -func count_failures() -> int: - return _reports.filter(__filter_is_failure).size() +static func count_failures(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_failure).size() -func count_errors() -> int: - return _reports.filter(__filter_is_error).size() +static func count_errors(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_error).size() -func count_warnings() -> int: - return _reports.filter(__filter_is_warning).size() +static func count_warnings(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_warning).size() -func count_skipped() -> int: - return _reports.filter(__filter_is_skipped).size() +static func count_skipped(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_skipped).size() func has_failures() -> bool: diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid index c96eccb3..06a78eb5 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid @@ -1 +1 @@ -uid://dqipby5q6bl3e +uid://dw25e1puhs5km diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd index b97f6fcd..e3fd510c 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd +++ b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd @@ -6,10 +6,11 @@ class_name GdUnitTestSuiteExecutor @warning_ignore("unused_private_class_variable") var _assertions := GdUnitAssertions.new() var _executeStage := GdUnitTestSuiteExecutionStage.new() - +var _debug_mode : bool func _init(debug_mode :bool = false) -> void: _executeStage.set_debug_mode(debug_mode) + _debug_mode = debug_mode func execute(test_suite :GdUnitTestSuite) -> void: @@ -23,6 +24,8 @@ func execute(test_suite :GdUnitTestSuite) -> void: func run_and_wait(tests: Array[GdUnitTestCase]) -> void: + if !_debug_mode: + GdUnitSignals.instance().gdunit_event.emit(GdUnitInit.new()) # first we group all tests by resource path var grouped_by_suites := GdArrayTools.group_by(tests, func(test: GdUnitTestCase) -> String: return test.suite_resource_path @@ -37,6 +40,8 @@ func run_and_wait(tests: Array[GdUnitTestCase]) -> void: await execute(test_suite) else: await GdUnit4CSharpApiLoader.execute(suite_tests) + if !_debug_mode: + GdUnitSignals.instance().gdunit_event.emit(GdUnitStop.new()) func fail_fast(enabled :bool) -> void: diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid index 995ba63b..867692d8 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid @@ -1 +1 @@ -uid://cemwa7djtp3pk +uid://b5jjatdmk0ljv diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd index 3515d721..d3c62455 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd @@ -18,30 +18,5 @@ func _execute(context: GdUnitExecutionContext) -> void: @warning_ignore("redundant_await") await test_suite.after_test() - await context.gc() + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.TEST_HOOK_AFTER) await context.error_monitor_stop() - - var reports := context.build_reports() - - if context.is_skipped(): - fire_test_skipped(context) - else: - fire_event(GdUnitEvent.new().test_after(context.test_case.id(), context.get_execution_statistics(), reports)) - - -func fire_test_skipped(context: GdUnitExecutionContext) -> void: - var test_case := context.test_case - var statistics := { - GdUnitEvent.ORPHAN_NODES: 0, - GdUnitEvent.ELAPSED_TIME: 0, - GdUnitEvent.WARNINGS: false, - GdUnitEvent.ERRORS: false, - GdUnitEvent.ERROR_COUNT: 0, - GdUnitEvent.FAILED: false, - GdUnitEvent.FAILED_COUNT: 0, - GdUnitEvent.SKIPPED: true, - GdUnitEvent.SKIPPED_COUNT: 1, - } - var report := GdUnitReport.new() \ - .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(test_case.skip_info())) - fire_event(GdUnitEvent.new().test_after(test_case.id(), statistics, [report])) diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid index fb6b8736..3ab7ee3c 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid @@ -1 +1 @@ -uid://gf00n84obhix +uid://d1cscmhjxxxn3 diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd index 8d22c100..4e04fad2 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd @@ -13,7 +13,6 @@ func _init(call_stage := true) -> void: func _execute(context :GdUnitExecutionContext) -> void: var test_suite := context.test_suite - fire_event(GdUnitEvent.new().test_before(context.test_case.id())) if _call_stage: @warning_ignore("redundant_await") await test_suite.before_test() diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid index 6d40e1e2..8ad7829f 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid @@ -1 +1 @@ -uid://bcyfa5mv5tjhc +uid://uwdyhemwuhyy diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid index 73cba8c9..fcb2cac1 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid @@ -1 +1 @@ -uid://csr4uurlyeln5 +uid://cp0aqeni5iuap diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd index a0f8ef19..03bbd0f7 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd @@ -12,16 +12,18 @@ func _execute(context :GdUnitExecutionContext) -> void: @warning_ignore("redundant_await") await test_suite.after() - await context.gc() - var reports := context.build_reports(false) + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.SUITE_HOOK_AFTER) + + var reports := context.collect_reports(false) + var statistics := context.calculate_statistics(reports) fire_event(GdUnitEvent.new()\ .suite_after(context.get_test_suite_path(),\ test_suite.get_name(), - context.get_execution_statistics(), + statistics, reports)) - GdUnitFileAccess.clear_tmp() # Guard that checks if all doubled (spy/mock) objects are released - GdUnitClassDoubler.check_leaked_instances() + await GdUnitClassDoubler.check_leaked_instances() # we hide the scene/main window after runner is finished - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) + if not Engine.is_embedded_in_editor(): + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid index c741566e..9d31bc7d 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid @@ -1 +1 @@ -uid://b7n0dyodt5ejc +uid://dyqjclpx0jwf2 diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid index 7bf378fb..ba8ef7e6 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid @@ -1 +1 @@ -uid://db7y7fek24cks +uid://b6nynmhgw171f diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd index 5b86b854..ac921e07 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd @@ -98,7 +98,8 @@ func fire_test_suite_skipped(context :GdUnitExecutionContext) -> void: continue var test_case_context := GdUnitExecutionContext.of_test_case(context, test_case) fire_event(GdUnitEvent.new().test_before(test_case.id())) - fire_test_skipped(test_case_context) + # use skip count 0 because we counted it over the complete test suite + fire_test_skipped(test_case_context, 0) var statistics := { @@ -117,7 +118,7 @@ func fire_test_suite_skipped(context :GdUnitExecutionContext) -> void: await (Engine.get_main_loop() as SceneTree).process_frame -func fire_test_skipped(context: GdUnitExecutionContext) -> void: +func fire_test_skipped(context: GdUnitExecutionContext, skip_count := 1) -> void: var test_case := context.test_case var statistics := { GdUnitEvent.ORPHAN_NODES: 0, @@ -128,7 +129,7 @@ func fire_test_skipped(context: GdUnitExecutionContext) -> void: GdUnitEvent.FAILED: false, GdUnitEvent.FAILED_COUNT: 0, GdUnitEvent.SKIPPED: true, - GdUnitEvent.SKIPPED_COUNT: 1, + GdUnitEvent.SKIPPED_COUNT: skip_count, } var report := GdUnitReport.new() \ .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped("Skipped from the entire test suite")) diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid index 7a1eb3c0..48808dc6 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid @@ -1 +1 @@ -uid://dvh7a6kwoykdr +uid://d2s8ig0jnlrqe diff --git a/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid index b4fcd877..0cd8c517 100644 --- a/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid @@ -1 +1 @@ -uid://8gbay4lwekw1 +uid://cdculr52bvtfs diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd index 243b889b..73a7a66e 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd @@ -8,6 +8,8 @@ var _stage_test :IGdUnitExecutionStage = GdUnitTestCaseFuzzedTestStage.new() func _execute(context :GdUnitExecutionContext) -> void: + fire_event(GdUnitEvent.new().test_before(context.test_case.id())) + while context.retry_execution(): var test_context := GdUnitExecutionContext.of(context) await _stage_before.execute(test_context) @@ -16,12 +18,35 @@ func _execute(context :GdUnitExecutionContext) -> void: await _stage_after.execute(test_context) if test_context.is_success() or test_context.is_skipped() or test_context.is_interupted(): break - @warning_ignore("return_value_discarded") - context.evaluate_test_retry_status() + context.gc() + if context.is_skipped(): + fire_test_skipped(context) + else: + var reports: = context.collect_reports(true) + var statistics := context.calculate_statistics(reports) + fire_event(GdUnitEvent.new().test_after(context.test_case.id(), statistics, reports)) func set_debug_mode(debug_mode :bool = false) -> void: super.set_debug_mode(debug_mode) _stage_before.set_debug_mode(debug_mode) _stage_after.set_debug_mode(debug_mode) _stage_test.set_debug_mode(debug_mode) + + +func fire_test_skipped(context: GdUnitExecutionContext) -> void: + var test_case := context.test_case + var statistics := { + GdUnitEvent.ORPHAN_NODES: 0, + GdUnitEvent.ELAPSED_TIME: 0, + GdUnitEvent.WARNINGS: false, + GdUnitEvent.ERRORS: false, + GdUnitEvent.ERROR_COUNT: 0, + GdUnitEvent.FAILED: false, + GdUnitEvent.FAILED_COUNT: 0, + GdUnitEvent.SKIPPED: true, + GdUnitEvent.SKIPPED_COUNT: 1, + } + var report := GdUnitReport.new() \ + .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(test_case.skip_info())) + fire_event(GdUnitEvent.new().test_after(test_case.id(), statistics, [report])) diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid index 474c0713..be43ae00 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid @@ -1 +1 @@ -uid://cbbbkqdxphdf3 +uid://cs0ryyahtw704 diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd index d32c28ed..62dda37e 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd @@ -33,7 +33,7 @@ func _execute(context :GdUnitExecutionContext) -> void: reports.append(GdUnitReport.new() \ .create(GdUnitReport.FAILURE, report.line_number(), GdAssertMessages.fuzzer_interuped(iteration, report.message()))) break - await context.gc() + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.TEST_CASE) # unguard on fuzzers if not test_case.is_interupted(): diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid index 2ac18c08..75e080cf 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid @@ -1 +1 @@ -uid://1ekg278uedk4 +uid://blq4ygrrxem0m diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd index 775b8dbc..70d687f4 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd @@ -9,6 +9,7 @@ var _stage_test :IGdUnitExecutionStage = GdUnitTestCaseSingleTestStage.new() func _execute(context :GdUnitExecutionContext) -> void: + fire_event(GdUnitEvent.new().test_before(context.test_case.id())) while context.retry_execution(): var test_context := GdUnitExecutionContext.of(context) await _stage_before.execute(test_context) @@ -17,8 +18,14 @@ func _execute(context :GdUnitExecutionContext) -> void: await _stage_after.execute(test_context) if test_context.is_success() or test_context.is_skipped() or test_context.is_interupted(): break - @warning_ignore("return_value_discarded") - context.evaluate_test_retry_status() + + context.gc() + if context.is_skipped(): + fire_test_skipped(context) + else: + var reports: = context.collect_reports(true) + var statistics := context.calculate_statistics(reports) + fire_event(GdUnitEvent.new().test_after(context.test_case.id(), statistics, reports)) func set_debug_mode(debug_mode :bool = false) -> void: @@ -26,3 +33,21 @@ func set_debug_mode(debug_mode :bool = false) -> void: _stage_before.set_debug_mode(debug_mode) _stage_after.set_debug_mode(debug_mode) _stage_test.set_debug_mode(debug_mode) + + +func fire_test_skipped(context: GdUnitExecutionContext) -> void: + var test_case := context.test_case + var statistics := { + GdUnitEvent.ORPHAN_NODES: 0, + GdUnitEvent.ELAPSED_TIME: 0, + GdUnitEvent.WARNINGS: false, + GdUnitEvent.ERRORS: false, + GdUnitEvent.ERROR_COUNT: 0, + GdUnitEvent.FAILED: false, + GdUnitEvent.FAILED_COUNT: 0, + GdUnitEvent.SKIPPED: true, + GdUnitEvent.SKIPPED_COUNT: 1, + } + var report := GdUnitReport.new() \ + .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(test_case.skip_info())) + fire_event(GdUnitEvent.new().test_after(test_case.id(), statistics, [report])) diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid index 87a7c2b2..71277d1e 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid @@ -1 +1 @@ -uid://bbmdy7h8gbcek +uid://bkvqbrbmlobl0 diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd index 6882fe29..9006b368 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd @@ -8,4 +8,4 @@ extends IGdUnitExecutionStage ## -> test_case() [br] func _execute(context :GdUnitExecutionContext) -> void: await context.test_case.execute() - await context.gc() + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.TEST_CASE) diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid index e8a6e443..f8aa39ef 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid @@ -1 +1 @@ -uid://c3anjw5j1bftg +uid://dpy8a60kt3tc0 diff --git a/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd new file mode 100644 index 00000000..0f87ad1b --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd @@ -0,0 +1,78 @@ +class_name GdUnitBaseReporterTestSessionHook +extends GdUnitTestSessionHook + + +var test_session: GdUnitTestSession: + get: + return test_session + set(value): + # disconnect first possible connected listener + if test_session != null: + test_session.test_event.disconnect(_on_test_event) + # add listening to current session + test_session = value + if test_session != null: + test_session.test_event.connect(_on_test_event) + + +var _report_summary: GdUnitReportSummary +var _reporter: GdUnitTestReporter +var _report_writer: GdUnitReportWriter +var _report_converter: Callable + +func _init(report_writer: GdUnitReportWriter, hook_name: String, hook_description: String, report_converter: Callable) -> void: + super(hook_name, hook_description) + _reporter = GdUnitTestReporter.new() + _report_writer = report_writer + _report_converter = report_converter + + +func startup(session: GdUnitTestSession) -> GdUnitResult: + test_session = session + _report_summary = GdUnitReportSummary.new(_report_converter) + _reporter.init_summary() + + return GdUnitResult.success() + + +func shutdown(session: GdUnitTestSession) -> GdUnitResult: + var report_path := _report_writer.write(session.report_path, _report_summary) + session.send_message("Open {0} Report at: file://{1}".format([_report_writer.output_format(), report_path])) + + return GdUnitResult.success() + + +func _on_test_event(event: GdUnitEvent) -> void: + match event.type(): + GdUnitEvent.TESTSUITE_BEFORE: + _reporter.init_statistics() + _report_summary.add_testsuite_report(event.resource_path(), event.suite_name(), event.total_count()) + GdUnitEvent.TESTSUITE_AFTER: + var statistics := _reporter.build_test_suite_statisitcs(event) + _report_summary.update_testsuite_counters( + event.resource_path(), + _reporter.error_count(statistics), + _reporter.failed_count(statistics), + _reporter.orphan_nodes(statistics), + _reporter.skipped_count(statistics), + _reporter.flaky_count(statistics), + event.elapsed_time()) + _report_summary.add_testsuite_reports( + event.resource_path(), + event.reports() + ) + GdUnitEvent.TESTCASE_BEFORE: + var test := test_session.find_test_by_id(event.guid()) + _report_summary.add_testcase(test.source_file, test.suite_name, test.display_name) + GdUnitEvent.TESTCASE_AFTER: + _reporter.add_test_statistics(event) + var test := test_session.find_test_by_id(event.guid()) + _report_summary.set_counters(test.source_file, + test.display_name, + event.error_count(), + event.failed_count(), + event.orphan_nodes(), + event.is_skipped(), + event.is_flaky(), + event.elapsed_time()) + _report_summary.add_reports(test.source_file, test.display_name, event.reports()) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd.uid new file mode 100644 index 00000000..dab8829d --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://7hbdcqdoqgkj diff --git a/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd new file mode 100644 index 00000000..4b8f390f --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd @@ -0,0 +1,9 @@ +class_name GdUnitHtmlReporterTestSessionHook +extends GdUnitBaseReporterTestSessionHook + +const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") + + +func _init() -> void: + super(GdUnitHtmlReportWriter.new(), "GdUnitHtmlTestReporter", "The Html test reporting hook.", GdUnitTools.richtext_normalize) + set_meta("SYSTEM_HOOK", true) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd.uid new file mode 100644 index 00000000..86b6d161 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://cqgptplj16q7q diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd new file mode 100644 index 00000000..23850e4a --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd @@ -0,0 +1,111 @@ +## @since GdUnit4 5.1.0 +## +## Base class for creating custom test session hooks in GdUnit4.[br] +## [br] +## [i]Test session hooks allow users to extend the GdUnit4 test framework by providing +## custom functionality that runs at specific points during the test execution lifecycle. +## This base class defines the interface that all test session hooks must implement.[/i] +## [br] +## [br] +## [b][u]Usage[/u][/b][br] +## 1. Create a new class that extends GdUnitTestSessionHook[br] +## 2. Override the required methods (startup, shutdown)[br] +## 3. Register your hook with the test engine (using the GdUnit4 settings dialog)[br] +## [br] +## [b][u]Example[/u][/b] +## [codeblock] +## class_name MyCustomTestHook +## extends GdUnitTestSessionHook +## +## func _init(): +## super("MyHook", "This is a description") +## +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Custom hook initialized") +## # Initialize resources, setup test environment, etc. +## return GdUnitResult.success() +## +## func shutdown(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Custom hook cleanup completed") +## # Cleanup resources, generate reports, etc. +## return GdUnitResult.success() +## [/codeblock] +## +## [b][u]Hook Lifecycle[/u][/b][br] +## 1. [i][b]Registration[/b][/i]: Hooks are registered with the test engine via settings dialog[br] +## 2. [i][b]Priority Sorting[/b][/i]: Hooks are sorted by priority[br] +## 3. [i][b]Startup[/b][/i]: startup() is called before test execution begins, if it returns an error is shown in the console[br] +## 4. [i][b]Test Execution[/b][/i]: Tests run normally (only if all hooks started successfully)[br] +## 5. [i][b]Shutdown[/b][/i]: shutdown() is called after all tests complete, regardless of startup success[br] +## [br] +## [b][u]Priority System[/u][/b][br] +## The priority system allows controlling the execution order of multiple hooks.[br] +## - The order can be changed in the GdUnit4 settings dialog.[br] +## - The priority of system hooks cannot be changed and they cannot be deleted.[br] +## [br] +## [b][u]Session Access[/u][/b][br] +## +## Both [i]startup()[/i] and [i]shutdown()[/i] methods receive a [GdUnitTestSession] parameter that provides:[br] +## - Access to test cases being executed[br] +## - Event emission capabilities for test progress tracking[br] +## - Message sending functionality for logging and communication[br] +class_name GdUnitTestSessionHook +extends RefCounted + + +## The display name of this hook. +var name: String: + get: + return name + + +## A detailed description of what this hook does. +var description: String: + get: + return description + + +## Initializes a new test session hook. +## +## [param _name] The display name for this hook +## [param _description] A detailed description of the hook's functionality +func _init(_name: String, _description: String) -> void: + self.name = _name + self.description = _description + + +## Called when the test session starts up, before any tests are executed.[br] +## [br] +## [color=yellow][i]This method should be overridden to implement custom initialization logic[/i][/color][br] +## [br] +## such as:[br] +## - Setting up test databases or external services[br] +## - Initializing mock objects or test fixtures[br] +## - Configuring logging or reporting systems[br] +## - Preparing the test environment[br] +## - Subscribing to test events via the session[br] +## [br] +## [param session] The test session instance providing access to test data and communication[br] +## [b]return:[/b] [code]GdUnitResult.success()[/code] if initialization succeeds, or [code]GdUnitResult.error("error")[/code] with +## an error message if initialization fails. +func startup(_session: GdUnitTestSession) -> GdUnitResult: + return GdUnitResult.error("%s:startup is not implemented" % get_script().resource_path) + + +## Called when the test session shuts down, after all tests have completed.[br] +## [br] +## [color=yellow][i]This method should be overridden to implement custom cleanup logic[/i][/color][br] +## [br] +## such as:[br] +## - Cleaning up test databases or external services[br] +## - Generating test reports or artifacts[br] +## - Releasing resources allocated during startup[br] +## - Performing final validation or assertions[br] +## - Processing collected test events and data[br] +## [br] +## [param session] The test session instance providing access to test results and communication[br] +## [b]return:[/b] [code]GdUnitResult.success()[/code] if cleanup succeeds, or [code]GdUnitResult.error("error")[/code] with +## an error message if cleanup fails. Cleanup errors are typically logged +## but don't prevent the test engine from shutting down. +func shutdown(_session: GdUnitTestSession) -> GdUnitResult: + return GdUnitResult.error("%s:shutdown is not implemented" % get_script().resource_path) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd.uid new file mode 100644 index 00000000..b6e2c390 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://d31ng640ty2si diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd new file mode 100644 index 00000000..5a9bdcb8 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd @@ -0,0 +1,191 @@ +class_name GdUnitTestSessionHookService +extends Object + + +var enigne_hooks: Array[GdUnitTestSessionHook] = []: + get: + return enigne_hooks + set(value): + enigne_hooks.append(value) + + +var _save_settings: bool = false + + +static func instance() -> GdUnitTestSessionHookService: + return GdUnitSingleton.instance("GdUnitTestSessionHookService", func()->GdUnitTestSessionHookService: + GdUnitSignals.instance().gdunit_message.emit("Installing GdUnit4 session system hooks.") + var service := GdUnitTestSessionHookService.new() + # Register default system hooks here + service._save_settings = false + service.register(GdUnitHtmlReporterTestSessionHook.new()) + service.register(GdUnitXMLReporterTestSessionHook.new()) + service.load_hook_settings() + service._save_settings = true + return service + ) + + +static func contains_hook(current: GdUnitTestSessionHook, other: GdUnitTestSessionHook) -> bool: + return current.get_script().resource_path == other.get_script().resource_path + + +func find_custom(hook: GdUnitTestSessionHook) -> int: + for index in enigne_hooks.size(): + if contains_hook.call(enigne_hooks[index], hook): + return index + return -1 + + +func load_hook(hook_resourc_path: String) -> GdUnitResult: + if !FileAccess.file_exists(hook_resourc_path): + return GdUnitResult.error("The hook '%s' not exists." % hook_resourc_path) + var script: GDScript = load(hook_resourc_path) + if script.get_base_script() != GdUnitTestSessionHook: + return GdUnitResult.error("The hook '%s' must inhertit from 'GdUnitTestSessionHook'." % hook_resourc_path) + + return GdUnitResult.success(script.new()) + + +func enable_hook(hook: GdUnitTestSessionHook, enabled: bool) -> void: + _enable_hook(hook, enabled) + GdUnitSignals.instance().gdunit_message.emit("Session hook '{name}' {enabled}.".format({ + "name": hook.name, + "enabled": "enabled" if enabled else "disabled"}) + ) + save_hock_setttings() + + +func register(hook: GdUnitTestSessionHook, enabled: bool = true) -> GdUnitResult: + if find_custom(hook) != -1: + return GdUnitResult.error("A hook instance of '%s' is already registered." % hook.get_script().resource_path) + + _enable_hook(hook, enabled) + enigne_hooks.append(hook) + save_hock_setttings() + GdUnitSignals.instance().gdunit_message.emit("Session hook '%s' installed." % hook.name) + + return GdUnitResult.success() + + +func unregister(hook: GdUnitTestSessionHook) -> GdUnitResult: + var hook_index := find_custom(hook) + if hook_index == -1: + return GdUnitResult.error("The hook instance of '%s' is NOT registered." % hook.get_script().resource_path) + + enigne_hooks.remove_at(hook_index) + save_hock_setttings() + return GdUnitResult.success() + + +func move_before(hook: GdUnitTestSessionHook, before: GdUnitTestSessionHook) -> void: + var before_index := find_custom(before) + var hook_index := find_custom(hook) + + # Verify the hook to move is behind the hook to be moved + if before_index >= hook_index: + return + + enigne_hooks.remove_at(hook_index) + enigne_hooks.insert(before_index, hook) + save_hock_setttings() + + +func move_after(hook: GdUnitTestSessionHook, after: GdUnitTestSessionHook) -> void: + var after_index := find_custom(after) + var hook_index := find_custom(hook) + + # Verify the hook to move is before the hook to be moved + if after_index <= hook_index: + return + + enigne_hooks.remove_at(hook_index) + enigne_hooks.insert(after_index, hook) + save_hock_setttings() + + +func execute_startup(session: GdUnitTestSession) -> GdUnitResult: + return await execute("startup", session) + + +func execute_shutdown(session: GdUnitTestSession) -> GdUnitResult: + return await execute("shutdown", session, true) + + +func execute(hook_func: String, session: GdUnitTestSession, reverse := false) -> GdUnitResult: + var failed_hook_calls: Array[GdUnitResult] = [] + + for hook_index in enigne_hooks.size(): + var index := enigne_hooks.size()-hook_index-1 if reverse else hook_index + var hook: = enigne_hooks[index] + if not is_enabled(hook): + continue + if OS.is_stdout_verbose(): + GdUnitSignals.instance().gdunit_message.emit("Session hook '%s' > %s()" % [hook.name, hook_func]) + var result: GdUnitResult = await hook.call(hook_func, session) + if result == null: + failed_hook_calls.push_back(GdUnitResult.error("Result is null! Check '%s'" % hook.get_script().resource_path)) + elif result.is_error(): + failed_hook_calls.push_back(result) + + if failed_hook_calls.is_empty(): + return GdUnitResult.success() + + var errors := failed_hook_calls.map(func(result: GdUnitResult) -> String: + return "Hook call '%s' failed with error: '%s'" % [hook_func, result.error_message()] + ) + return GdUnitResult.error( "\n".join(errors)) + + +func save_hock_setttings() -> void: + if not _save_settings: + return + + var hooks_to_save: Dictionary[String, bool] = {} + for hook in enigne_hooks: + var enabled: bool = hook.get_meta("enabled") + hooks_to_save[hook.get_script().resource_path] = enabled + + GdUnitSettings.set_session_hooks(hooks_to_save) + + +func load_hook_settings() -> void: + var hooks_resource_paths := GdUnitSettings.get_session_hooks() + if hooks_resource_paths.is_empty(): + return + + for hock_path: String in hooks_resource_paths.keys(): + var enabled := hooks_resource_paths[hock_path] + + # Do not reinstall already installed hooks + var existing_hook: GdUnitTestSessionHook = enigne_hooks.filter(func(element: GdUnitTestSessionHook) -> bool: + return element.get_script().resource_path == hock_path + ).front() + # Applay enabled settings + if existing_hook != null: + _enable_hook(existing_hook, enabled) + continue + + # Load additional hooks + var result := load_hook(hock_path) + if result.is_error(): + push_error(result.error_message()) + continue + + GdUnitSignals.instance().gdunit_message.emit("Installing GdUnit4 session hooks.") + var hook: GdUnitTestSessionHook = result.value() + + result = register(hook, enabled) + if result.is_error(): + push_error(result.error_message()) + continue + + +static func is_enabled(hook: GdUnitTestSessionHook) -> bool: + if hook.has_meta("enabled"): + return hook.get_meta("enabled") + return true + + +func _enable_hook(hook: GdUnitTestSessionHook, enabled: bool) -> void: + hook.set_meta("enabled", enabled) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd.uid new file mode 100644 index 00000000..169b8083 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd.uid @@ -0,0 +1 @@ +uid://dqaa2hqklm08m diff --git a/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd new file mode 100644 index 00000000..94caef59 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd @@ -0,0 +1,11 @@ +class_name GdUnitXMLReporterTestSessionHook +extends GdUnitBaseReporterTestSessionHook + + +func _init() -> void: + super(JUnitXmlReportWriter.new(), "GdUnitXMLTestReporter", "The JUnit XML test reporting hook.", convert_report_message) + set_meta("SYSTEM_HOOK", true) + + +func convert_report_message(value: String) -> String: + return value diff --git a/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd.uid new file mode 100644 index 00000000..113b9a88 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://br3xruj0x8qmq diff --git a/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid b/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid index 66e1440e..85a58eb0 100644 --- a/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid @@ -1 +1 @@ -uid://drfsucjb08nqc +uid://bym7i6nltalyn diff --git a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd index de8c29a6..f1b22440 100644 --- a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd +++ b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd @@ -21,7 +21,7 @@ var _decoders := { TYPE_PACKED_COLOR_ARRAY: _on_type_Array.bind(TYPE_PACKED_COLOR_ARRAY), TYPE_PACKED_VECTOR2_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR2_ARRAY), TYPE_PACKED_VECTOR3_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR3_ARRAY), - GdObjects.TYPE_PACKED_VECTOR4_ARRAY: _on_type_Array.bind(GdObjects.TYPE_PACKED_VECTOR4_ARRAY), + TYPE_PACKED_VECTOR4_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR4_ARRAY), TYPE_DICTIONARY: _on_type_Dictionary, TYPE_RID: _on_type_RID, TYPE_NODE_PATH: _on_type_NodePath, @@ -126,7 +126,7 @@ func _on_type_Array(value: Variant, type: int) -> String: return "PackedVector3Array()" return "PackedVector3Array([%s])" % ", ".join(vectors) - GdObjects.TYPE_PACKED_VECTOR4_ARRAY: + TYPE_PACKED_VECTOR4_ARRAY: var vectors := PackedStringArray() for vector: Vector4 in value: @warning_ignore("return_value_discarded") diff --git a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid index 3c00cc90..0ee505a2 100644 --- a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid @@ -1 +1 @@ -uid://skwtjom3d5me +uid://dwh66p2rypwe0 diff --git a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd index 1cc27dbf..fdc1c433 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd +++ b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd @@ -42,7 +42,7 @@ func name() -> String: func default() -> Variant: - return GodotVersionFixures.convert(_default_value, _type) + return type_convert(_default_value, _type) func set_value(value: String) -> void: diff --git a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid index b9181228..95c51b9a 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid @@ -1 +1 @@ -uid://cjfu1pu4710el +uid://d4dvpborpriat diff --git a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd index 515c92a5..acfada76 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd +++ b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd @@ -101,6 +101,8 @@ func return_type() -> int: func return_type_as_string() -> String: + if return_type() == TYPE_NIL: + return "void" if (return_type() == TYPE_OBJECT or return_type() == GdObjects.TYPE_ENUM) and not _return_class.is_empty(): return _return_class return GdObjects.type_as_string(return_type()) @@ -110,7 +112,17 @@ func set_argument_value(arg_name: String, value: String) -> void: var argument: GdFunctionArgument = _args.filter(func(arg: GdFunctionArgument) -> bool: return arg.name() == arg_name ).front() - argument.set_value(value) + if argument != null: + argument.set_value(value) + + +func enrich_arguments(arguments: Array[Dictionary]) -> void: + for arg_index: int in arguments.size(): + var arg: Dictionary = arguments[arg_index] + if arg["type"] != GdObjects.TYPE_VARARG: + var arg_name: String = arg["name"] + var arg_value: String = arg["value"] + set_argument_value(arg_name, arg_value) func enrich_file_info(p_source_path: String, p_line_number: int) -> void: @@ -225,10 +237,7 @@ static func _build_varargs(p_is_vararg :bool) -> Array[GdFunctionArgument]: var varargs_ :Array[GdFunctionArgument] = [] if not p_is_vararg: return varargs_ - # if function has vararg we need to handle this manually by adding 10 default arguments - var type := GdObjects.TYPE_VARARG - for index in 10: - varargs_.push_back(GdFunctionArgument.new("vararg%d_" % index, type, '"%s"' % GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE)) + varargs_.push_back(GdFunctionArgument.new("varargs", GdObjects.TYPE_VARARG, '')) return varargs_ diff --git a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid index 354b7d95..0276fcba 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid @@ -1 +1 @@ -uid://cswdja6rwhc4f +uid://bmh8wp8hnsbco diff --git a/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid b/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid index aa6707fd..58c12d87 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid @@ -1 +1 @@ -uid://bj35ad04v6rr4 +uid://c6mb8lwv51vuh diff --git a/addons/gdUnit4/src/core/parse/GdScriptParser.gd b/addons/gdUnit4/src/core/parse/GdScriptParser.gd index 7f8c7031..16f54cc9 100644 --- a/addons/gdUnit4/src/core/parse/GdScriptParser.gd +++ b/addons/gdUnit4/src/core/parse/GdScriptParser.gd @@ -3,7 +3,13 @@ extends RefCounted const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") -const ALLOWED_CHARACTERS := "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"" +const TYPE_VOID = GdObjects.TYPE_VOID +const TYPE_VARIANT = GdObjects.TYPE_VARIANT +const TYPE_VARARG = GdObjects.TYPE_VARARG +const TYPE_FUNC = GdObjects.TYPE_FUNC +const TYPE_FUZZER = GdObjects.TYPE_FUZZER +const TYPE_ENUM = GdObjects.TYPE_ENUM + var TOKEN_NOT_MATCH := Token.new("") var TOKEN_SPACE := SkippableToken.new(" ") @@ -23,6 +29,7 @@ var TOKEN_ARGUMENT_ASIGNMENT := Token.new("=") var TOKEN_ARGUMENT_TYPE_ASIGNMENT := Token.new(":=") var TOKEN_ARGUMENT_FUZZER := FuzzerToken.new(GdUnitTools.to_regex("((?!(fuzzer_(seed|iterations)))fuzzer?\\w+)( ?+= ?+| ?+:= ?+| ?+:Fuzzer ?+= ?+|)")) var TOKEN_ARGUMENT_TYPE := Token.new(":") +var TOKEN_ARGUMENT_VARIADIC := Token.new("...") var TOKEN_ARGUMENT_SEPARATOR := Token.new(",") var TOKEN_BRACKET_ROUND_OPEN := Token.new("(") var TOKEN_BRACKET_ROUND_CLOSE := Token.new(")") @@ -60,6 +67,7 @@ var TOKENS :Array[Token] = [ TOKEN_ARGUMENT_TYPE_ASIGNMENT, TOKEN_ARGUMENT_ASIGNMENT, TOKEN_ARGUMENT_TYPE, + TOKEN_ARGUMENT_VARIADIC, TOKEN_FUNCTION, TOKEN_ARGUMENT_SEPARATOR, TOKEN_FUNCTION_RETURN_TYPE, @@ -71,6 +79,7 @@ var TOKENS :Array[Token] = [ ] var _regex_clazz_name := GdUnitTools.to_regex("(class) ([a-zA-Z0-9_]+) (extends[a-zA-Z]+:)|(class) ([a-zA-Z0-9_]+)") +var _regex_func_name := GdUnitTools.to_regex("^(?:static\\s+)?func\\s+([\\w\\p{L}\\p{N}_]+)\\s*\\(") var _regex_strip_comments := GdUnitTools.to_regex("^([^#\"']|'[^']*'|\"[^\"]*\")*\\K#.*") var _scanned_inner_classes := PackedStringArray() var _script_constants := {} @@ -303,6 +312,7 @@ func get_token(input :String, current_index :int) -> Token: func next_token(input: String, current_index: int, ignore_tokens :Array[Token] = []) -> Token: var token := TOKEN_NOT_MATCH for t :Token in TOKENS.filter(func(t :Token) -> bool: return not ignore_tokens.has(t)): + if t.match(input, current_index): token = t break @@ -326,7 +336,7 @@ func tokenize_value(input: String, current: int, token: Token, ignore_dots := fa # or allowend charset # or is a float value if (test_for_sign and next==0) \ - or character in ALLOWED_CHARACTERS \ + or is_allowed_character(character) \ or (character == "." and (ignore_dots or current_token.is_valid_int())): current_token += character next += 1 @@ -337,6 +347,31 @@ func tokenize_value(input: String, current: int, token: Token, ignore_dots := fa return TOKEN_NOT_MATCH +# const ALLOWED_CHARACTERS := "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"" +func is_allowed_character(input: String) -> bool: + var code_point := input.unicode_at(0) + # Unicode + if code_point > 127: + # This is a Unicode character (Chinese, Japanese, etc.) + return true + # ASCII digit 0-9 + if code_point >= 48 and code_point <= 57: + return true + # ASCII lowercase a-z + if code_point >= 97 and code_point <= 122: + return true + # ASCII uppercase A-Z + if code_point >= 65 and code_point <= 90: + return true + # underscore _ + if code_point == 95: + return true + # quotes '" + if code_point == 34 or code_point == 39: + return true + return false + + func extract_clazz_name(value :String) -> String: var result := _regex_clazz_name.search(value) if result == null: @@ -390,12 +425,14 @@ func is_getter_or_setter(func_name: String) -> bool: return func_name.begins_with("@") and (func_name.ends_with("getter") or func_name.ends_with("setter")) -func _parse_function_arguments(input: String) -> Dictionary: - var arguments := {} +func _parse_function_arguments(input: String) -> Array[Dictionary]: + var arguments: Array[Dictionary] = [] var current_index := 0 - var token :Token = null + var token: Token = null var bracket := 0 var in_function := false + + while current_index < len(input): token = next_token(input, current_index) # fallback to not end in a endless loop @@ -417,7 +454,6 @@ func _parse_function_arguments(input: String) -> Dictionary: if token == TOKEN_BRACKET_ROUND_OPEN : in_function = true bracket += 1 - continue if token == TOKEN_BRACKET_ROUND_CLOSE: bracket -= 1 # if function end? @@ -428,17 +464,16 @@ func _parse_function_arguments(input: String) -> Dictionary: token = next_token(input, current_index) current_index += token._consumed continue - # is fuzzer argument - if token is FuzzerToken: - var arg_value := _parse_end_function(input.substr(current_index), true) - current_index += arg_value.length() - var arg_name :String = (token as FuzzerToken).name() - arguments[arg_name] = arg_value.lstrip(" ") - continue + # is value argument - if in_function and token.is_variable(): - var arg_name: String = (token as Variable).plain_value() - var arg_value: String = GdFunctionArgument.UNDEFINED + if in_function: + var arg_value := "" + var current_argument := { + "name" : "", + "value" : GdFunctionArgument.UNDEFINED, + "type" : TYPE_VARIANT + } + # parse type and default value while current_index < len(input): token = next_token(input, current_index) @@ -446,19 +481,44 @@ func _parse_function_arguments(input: String) -> Dictionary: if token.is_skippable(): continue + if token.is_variable() && current_argument["name"] == "": + arguments.append(current_argument) + current_argument["name"] = (token as Variable).plain_value() + continue + match token: + # is fuzzer argument + TOKEN_ARGUMENT_FUZZER: + arg_value = _parse_end_function(input.substr(current_index), true) + current_index += arg_value.length() + current_argument["name"] = (token as FuzzerToken).name() + current_argument["value"] = arg_value.lstrip(" ") + current_argument["type"] = TYPE_FUZZER + arguments.append(current_argument) + continue + + TOKEN_ARGUMENT_VARIADIC: + current_argument["type"] = TYPE_VARARG + TOKEN_ARGUMENT_TYPE: token = next_token(input, current_index) if token == TOKEN_SPACE: current_index += token._consumed token = next_token(input, current_index) + current_index += token._consumed + if current_argument["type"] != TYPE_VARARG: + current_argument["type"] = GdObjects.string_to_type((token as Variable).plain_value()) + TOKEN_ARGUMENT_TYPE_ASIGNMENT: arg_value = _parse_end_function(input.substr(current_index), true) current_index += arg_value.length() + current_argument["value"] = arg_value.lstrip(" ") TOKEN_ARGUMENT_ASIGNMENT: token = next_token(input, current_index) arg_value = _parse_end_function(input.substr(current_index), true) current_index += arg_value.length() + current_argument["value"] = arg_value.lstrip(" ") + TOKEN_BRACKET_SQUARE_OPEN: bracket += 1 TOKEN_BRACKET_CURLY_OPEN: @@ -484,8 +544,13 @@ func _parse_function_arguments(input: String) -> Dictionary: break TOKEN_ARGUMENT_SEPARATOR: if bracket <= 1: - break - arguments[arg_name] = arg_value.lstrip(" ") + # next argument + current_argument = { + "name" : "", + "value" : GdFunctionArgument.UNDEFINED, + "type" : GdObjects.TYPE_VARIANT + } + continue return arguments @@ -557,7 +622,7 @@ func extract_inner_class(source_rows: PackedStringArray, clazz_name :String) -> return PackedStringArray() -func extract_func_signature(rows :PackedStringArray, index :int) -> String: +func extract_func_signature(rows: PackedStringArray, index: int) -> String: var signature := "" for rowIndex in range(index, rows.size()): @@ -589,22 +654,18 @@ func get_class_name(script :GDScript) -> String: return GdObjects.to_pascal_case(script.resource_path.get_basename().get_file()) -func parse_func_name(input :String) -> String: - var current_index := 0 - var token := next_token(input, current_index) - current_index += token._consumed - if token != TOKEN_FUNCTION_STATIC_DECLARATION and token != TOKEN_FUNCTION_DECLARATION: +func parse_func_name(input: String) -> String: + var result := _regex_func_name.search(input) + if result == null: + push_error("Can't extract function name from '%s'" % input) return "" - while not token is Variable: - token = next_token(input, current_index) - current_index += token._consumed - return token._token + return result.get_string(1) ## Enriches the function descriptor by line number and argument default values ## - enrich all function descriptors form current script up to all inherited scrips func _enrich_function_descriptor(script: GDScript, fds: Array[GdFunctionDescriptor]) -> void: - var enriched_functions := PackedStringArray() + var enriched_functions := {} # Use Dictionary for O(1) lookup instead of PackedStringArray var script_to_scan := script while script_to_scan != null: # do not scan the test suite base class itself @@ -621,29 +682,35 @@ func _enrich_function_descriptor(script: GDScript, fds: Array[GdFunctionDescript if input.begins_with("#") or input.length() == 0: continue var token := next_token(input, 0) - if token == TOKEN_FUNCTION_STATIC_DECLARATION or token == TOKEN_FUNCTION_DECLARATION: - var function_name := parse_func_name(input) - var fd: GdFunctionDescriptor = fds.filter(func(element: GdFunctionDescriptor) -> bool: - # is same function name and not already enriched - return function_name == element.name() and not enriched_functions.has(element.name()) - ).pop_front() - if fd != null: - # add as enriched function to exclude from next iteration (could be inherited) - @warning_ignore("return_value_discarded") - enriched_functions.append(fd.name()) - var func_signature := extract_func_signature(rows, rowIndex) - var func_arguments := _parse_function_arguments(func_signature) - # enrich missing default values - for arg_name: String in func_arguments.keys(): - var func_argument: String = func_arguments[arg_name] - fd.set_argument_value(arg_name, func_argument) - fd.enrich_file_info(script_to_scan.resource_path, rowIndex + 1) - fd._is_coroutine = is_func_coroutine(rows, rowIndex) - # enrich return class name if not set - if fd.return_type() == TYPE_OBJECT and fd._return_class in ["", "Resource", "RefCounted"]: - var var_token := parse_return_token(func_signature) - if var_token != TOKEN_NOT_MATCH and var_token.type() == TYPE_OBJECT: - fd._return_class = _patch_inner_class_names(var_token.plain_value(), "") + if token != TOKEN_FUNCTION_STATIC_DECLARATION and token != TOKEN_FUNCTION_DECLARATION: + continue + + var function_name := parse_func_name(input) + # Skip if already enriched (from parent class scan) + if enriched_functions.has(function_name): + continue + + # Find matching function descriptor + var fd: GdFunctionDescriptor = null + for candidate in fds: + if candidate.name() == function_name: + fd = candidate + break + if fd == null: + continue + # Mark as enriched + enriched_functions[function_name] = true + var func_signature := extract_func_signature(rows, rowIndex) + var func_arguments := _parse_function_arguments(func_signature) + # enrich missing default values + fd.enrich_arguments(func_arguments) + fd.enrich_file_info(script_to_scan.resource_path, rowIndex + 1) + fd._is_coroutine = is_func_coroutine(rows, rowIndex) + # enrich return class name if not set + if fd.return_type() == TYPE_OBJECT and fd._return_class in ["", "Resource", "RefCounted"]: + var var_token := parse_return_token(func_signature) + if var_token != TOKEN_NOT_MATCH and var_token.type() == TYPE_OBJECT: + fd._return_class = _patch_inner_class_names(var_token.plain_value(), "") # if the script ihnerits we need to scan this also script_to_scan = script_to_scan.get_base_script() diff --git a/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid b/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid index 07668e31..d1f37021 100644 --- a/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid @@ -1 +1 @@ -uid://i668uoaxapyq +uid://c6aa25smqb4sc diff --git a/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid b/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid index 47feae62..25ad1dcc 100644 --- a/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid @@ -1 +1 @@ -uid://biv2o8ilvsise +uid://bwn8cir6msdfp diff --git a/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid b/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid index 3e187dee..13c053ed 100644 --- a/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid @@ -1 +1 @@ -uid://bfnwwm8qd72dm +uid://b7lc87dr4aad2 diff --git a/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid b/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid index 4ff42ab8..2b6cfec9 100644 --- a/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid +++ b/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid @@ -1 +1 @@ -uid://c3mgwbe5nn5jh +uid://dpju4vx120vhk diff --git a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid deleted file mode 100644 index e3228044..00000000 --- a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://defcf5pbdoxmd diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd index 608f02b9..34dcfa38 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd +++ b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd @@ -1,6 +1,6 @@ #warning-ignore-all:return_value_discarded class_name GdUnitTestCIRunner -extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" +extends "res://addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd" ## Command line test runner implementation.[br] ## [br] ## This runner is designed for CI/CD pipelines and command line test execution.[br] @@ -23,10 +23,7 @@ extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") var _console := GdUnitCSIMessageWriter.new() -var _console_reporter: GdUnitTestReporter -var _html_reporter: GdUnitHtmlTestReporter -var _report_dir: String -var _report_max: int = DEFAULT_REPORT_COUNT +var _console_reporter: GdUnitConsoleTestReporter var _headless_mode_ignore := false var _runner_config_file := "" var _debug_cmd_args := PackedStringArray() @@ -80,7 +77,7 @@ var _cmd_options := CmdOptions.new([ CmdOption.new( "-rc, --report-count", "-rc ", - "Specifies how many reports are saved before they are deleted. The default is %s." % str(DEFAULT_REPORT_COUNT), + "Specifies how many reports are saved before they are deleted. The default is %s." % str(GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT), TYPE_INT, true ), @@ -108,10 +105,10 @@ func _init() -> void: func _ready() -> void: super() - _report_dir = GdUnitFileAccess.current_dir() + "reports" - _console_reporter = GdUnitConsoleTestReporter.new(_console, true) # stop checked first test failure to fail fast _executor.fail_fast(true) + _console_reporter = GdUnitConsoleTestReporter.new(_console, true) + GdUnitSignals.instance().gdunit_message.connect(_on_send_message) func _notification(what: int) -> void: @@ -134,8 +131,7 @@ func get_exit_code() -> int: ## [br] ## [param code] The exit code to return. func quit(code: int) -> void: - if code != RETURN_SUCCESS: - _state = EXIT + _state = EXIT GdUnitTools.dispose_all() await GdUnitMemoryObserver.gc_on_guarded_instances() await super(code) @@ -167,9 +163,9 @@ func console_warning(message: String) -> void: ## [br] ## [param path] The path where reports should be written. func set_report_dir(path: String) -> void: - _report_dir = ProjectSettings.globalize_path(GdUnitFileAccess.make_qualified_path(path)) + report_base_path = ProjectSettings.globalize_path(GdUnitFileAccess.make_qualified_path(path)) console_info( - "Set write reports to %s" % _report_dir, + "Set write reports to %s" % report_base_path, Color.DEEP_SKY_BLUE ) @@ -182,15 +178,15 @@ func set_report_count(count: String) -> void: if report_count < 1: console_error( "Invalid report history count '%s' set back to default %d" - % [count, DEFAULT_REPORT_COUNT] + % [count, GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT] ) - _report_max = DEFAULT_REPORT_COUNT + max_report_history = GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT else: console_info( "Set report history count to %s" % count, Color.DEEP_SKY_BLUE ) - _report_max = report_count + max_report_history = report_count ## Disables fail-fast mode to run all tests.[br] @@ -387,13 +383,12 @@ func init_gd_unit() -> void: quit(RETURN_ERROR_HEADLESS_NOT_SUPPORTED) return - _html_reporter = GdUnitHtmlTestReporter.new(_report_dir, _report_max) - discover_tests() + _test_cases = discover_tests() if _test_cases.is_empty(): console_info("No test cases found, abort test run!", Color.YELLOW) console_info("Exit code: %d" % RETURN_SUCCESS, Color.DARK_SALMON) quit(RETURN_SUCCESS) - _on_gdunit_event(GdUnitInit.new()) + return _state = RUN @@ -437,13 +432,14 @@ func is_skipped(test: GdUnitTestCase) -> bool: # is suite skipped by full path or suite name if skipped_info == test.suite_name or test.source_file.contains(skipped_info): return true + var skip_file := skipped_info.replace("res://", "") # check for skipped single test - if not skipped_info.contains(":"): + if not skip_file.contains(":"): continue - var parts: PackedStringArray = skipped_info.rsplit(":") - var skipped_suite := parts[0] + ":" + parts[1] if parts[0] == "res" else parts[0] - var skipped_test := parts[2] if parts[0] == "res" else parts[1] + var parts: PackedStringArray = skip_file.rsplit(":") + var skipped_suite := parts[0] + var skipped_test := parts[1] # is suite skipped by full path or suite name if (skipped_suite == test.suite_name or test.source_file.contains(skipped_suite)) and skipped_test == test.test_name: return true @@ -451,18 +447,16 @@ func is_skipped(test: GdUnitTestCase) -> bool: return false -func _on_gdunit_event(event: GdUnitEvent) -> void: - _console_reporter.on_gdunit_event(event) - _html_reporter.on_gdunit_event(event) +func _on_send_message(message: String) -> void: + _console.color(Color.CORNFLOWER_BLUE).println_message(message) + +func _on_gdunit_event(event: GdUnitEvent) -> void: match event.type(): - GdUnitEvent.STOP: - # TODO move to `GdUnitJUnitXMLTestReporter` - JUnitXmlReport.new(_html_reporter._report._report_path, _html_reporter._report.iteration()).write(_html_reporter._report) - console_info( - "Open HTML Report at: file://%s" % _html_reporter.report_file(), - Color.CORNFLOWER_BLUE - ) + GdUnitEvent.SESSION_START: + _console_reporter.test_session = _test_session + GdUnitEvent.SESSION_CLOSE: + _console_reporter.test_session = null func report_exit_code() -> int: diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid index 9965d214..8631a77a 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid +++ b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid @@ -1 +1 @@ -uid://di32h4jml3fgu +uid://vmbyg1sso3x diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd index 2d139721..5af6c9b9 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd +++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd @@ -1,4 +1,4 @@ -extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" +extends "res://addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd" ## Runner implementation used by the editor UI.[br] ## [br] ## This runner connects to a GdUnit server via TCP to report test results.[br] @@ -8,17 +8,20 @@ extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" ## - Messages to report progress[br] ## - Events to report test results[br] - ## The TCP client used to connect to the GdUnit server @onready var _client: GdUnitTcpClient = $GdUnitTcpClient +@onready var _version_label: Control = %Version func _init() -> void: super() + # We set the default max report history to 1 + max_report_history = 1 func _ready() -> void: super() + GdUnit4Version.init_version_label(_version_label) var config_result := _runner_config.load_config() if config_result.is_error(): @@ -27,12 +30,22 @@ func _ready() -> void: return @warning_ignore("return_value_discarded") _client.connect("connection_failed", _on_connection_failed) + GdUnitSignals.instance().gdunit_message.connect(_on_send_message) var result := _client.start("127.0.0.1", _runner_config.server_port()) if result.is_error(): push_error(result.error_message()) return +## Cleanup and quit the runner.[br] +## [br] +## [param code] The exit code to return. +func quit(code: int) -> void: + if code != RETURN_SUCCESS: + _state = EXIT + await GdUnitMemoryObserver.gc_on_guarded_instances() + + ## Called when the TCP connection to the GdUnit server fails.[br] ## Stops the test execution.[br] ## [br] @@ -48,7 +61,7 @@ func _on_connection_failed(message: String) -> void: func init_runner() -> void: # wait until client is connected to the GdUnitServer if _client.is_client_connected(): - gdUnitInit() + await gdUnitInit() _state = RUN @@ -63,7 +76,7 @@ func gdUnitInit() -> void: ## Sends a message via TCP to the GdUnit server.[br] ## [br] ## [param message] The message to send. -func send_message(message: String) -> void: +func _on_send_message(message: String) -> void: _client.send(RPCMessage.of(message)) diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid index 84c5b25f..7ea1f0b7 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid +++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid @@ -1 +1 @@ -uid://2wcllsu5cjqf +uid://cj8meyhbirnec diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn index 615f2e13..eaa6f1ab 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn +++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn @@ -1,10 +1,34 @@ [gd_scene load_steps=3 format=3 uid="uid://belidlfknh74r"] -[ext_resource type="Script" uid="uid://2wcllsu5cjqf" path="res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd" id="1"] -[ext_resource type="Script" uid="uid://87va3sa5psi1" path="res://addons/gdUnit4/src/network/GdUnitTcpClient.gd" id="2"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitTcpClient.gd" id="2"] [node name="Control" type="Node"] script = ExtResource("1") [node name="GdUnitTcpClient" type="Node" parent="."] script = ExtResource("2") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +custom_minimum_size = Vector2(0, 24) +layout_direction = 2 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 0 +size_flags_horizontal = 3 +size_flags_vertical = 10 +alignment = 2 + +[node name="Version" type="RichTextLabel" parent="HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(128, 0) +layout_mode = 2 +size_flags_horizontal = 10 +bbcode_enabled = true +scroll_active = false +shortcut_keys_enabled = false +horizontal_alignment = 1 +justification_flags = 0 diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd new file mode 100644 index 00000000..c789847b --- /dev/null +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd @@ -0,0 +1,169 @@ +## +## @since GdUnit4 5.1.0 +## +## Represents a test execution session in GdUnit4.[br] +## [br] +## [i]A test session encapsulates a complete test execution cycle, managing the collection +## of test cases to be executed and providing communication channels for test events +## and messages. This class serves as the central coordination point for test execution +## and allows hooks and other components to interact with the running test session.[/i][br] +## [br] +## [b][u]Key Features[/u][/b][br] +## - [i][b]Test Case Management[/b][/i]: Maintains a collection of test cases to be executed[br] +## - [i][b]Event Broadcasting[/b][/i]: Forwards GdUnit events to session-specific listeners[br] +## - [i][b]Message Communication[/b][/i]: Provides a channel for sending messages during test execution[br] +## - [i][b]Hook Integration[/b][/i]: Passed to test session hooks for startup and shutdown operations[br] +## [br] +## [b][u]Usage in Test Hooks[/u][/b] +## [codeblock] +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## # Access test cases +## print("Running %d test cases" % session.test_cases.size()) +## +## # Send status messages +## session.send_message("Custom hook initialized") +## +## # Listen for test events +## session.test_event.connect(_on_test_event) +## +## return GdUnitResult.success() +## +## func _on_test_event(event: GdUnitEvent) -> void: +## print("Test event received: %s" % event.type) +## [/codeblock] +## [br] +## [b][u]Event Flow[/u][/b][br] +## 1. Session is created with a collection of test cases[br] +## 2. Session connects to the global GdUnit event system[br] +## 3. During test execution, events are automatically forwarded to session listeners[br] +## 4. Hooks and other components can subscribe to session events[br] +## 5. Messages can be sent through the session for logging and communication[br] +class_name GdUnitTestSession +extends RefCounted + + +## Emitted when a test execution event occurs.[br] +## [br] +## [i]This signal forwards events from the global GdUnit event system to session-specific +## listeners. It allows hooks and other session components to react to test events +## without directly connecting to the global event system.[/i][br] +## [br] +## [u]Common event types include:[/u][br] +## - Test suite start/end events[br] +## - Test case start/end events[br] +## - Test assertion events[br] +## - Test failure/error events[br] +## +## [param event] The test event containing details about test execution, timing, and results +@warning_ignore("unused_signal") +signal test_event(event: GdUnitEvent) + + +## [b][color=red]@readonly: Should not be modified directly during test execution![/color][/b][br] +## Collection of test cases to be executed in this session.[br] +## [br] +## This array contains all the test cases that will be run during the session. +## Test hooks can access this collection to: +## - Get the total number of tests to be executed +## - Access individual test case metadata +## - Perform setup/teardown based on test case requirements +## - Generate reports or statistics about the test suite +## +## The collection is typically populated before session startup and remains +## constant during test execution. +var _test_cases : Array[GdUnitTestCase] = [] + + +## [b][color=red]@readonly: The report path should not be modified after session creation![/color][/b][br] +## The file system path where test reports for this session will be generated.[br] +## [br] +## [i]This property provides centralized access to the report output location, +## allowing test hooks, reporters, and other components to reference the same +## report path without coupling to specific reporter implementations.[/i][br] +## [br] +## [b][u]Common use cases include:[/u][/b][br] +## - Test hooks generating additional report files in the same directory[br] +## - Custom reporters creating supplementary output files[br] +## - Post-processing scripts that need to locate generated reports[br] +## - Cleanup operations that need to manage report artifacts[br] +## [br] +## [b][u]Example Usage:[/u][/b] +## [codeblock] +## # In a test hook +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## var report_dir = session.report_path.get_base_dir() +## var custom_report = report_dir.path_join("custom_metrics.json") +## # Generate additional reports in the same location +## return GdUnitResult.success() +## +## func shutdown(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Reports available at: " + session.report_path) +## return GdUnitResult.success() +## [/codeblock] +## [br] +## The path is set during session initialization and remains constant throughout +## the test execution lifecycle. +var report_path: String: + get: + return report_path + + +## Initializes the test session and sets up event forwarding.[br] +## [br] +## [i]This constructor automatically connects to the global GdUnit event system +## and forwards all events to the session's test_event signal. This allows +## session-specific components to listen for test events without managing +## global signal connections.[/i] +func _init(test_cases: Array[GdUnitTestCase], session_report_path: String) -> void: + # We build a copy to prevent a user is modifing the tests + _test_cases = test_cases.duplicate(true) + report_path = session_report_path + GdUnitSignals.instance().gdunit_event.connect(func(event: GdUnitEvent) -> void: + test_event.emit(event) + ) + + +## Finds a test case by its unique identifier.[br] +## [br] +## [i]Searches through all test cases to find a test with the matching GUID.[/i][br] +## [br] +## [param id] The GUID of the test to find[br] +## Returns the matching test case or null if not found. +func find_test_by_id(id: GdUnitGUID) -> GdUnitTestCase: + for test in _test_cases: + if test.guid.equals(id): + return test + + return null + + +## Sends a message through the GdUnit messaging system.[br] +## [br] +## [i]This method provides a convenient way for test hooks and other session +## components to send messages that will be handled by the GdUnit framework.[/i] +## [br][br] +## [b][u]Messages are typically used for:[/u][/b][br] +## - Status updates during test execution[br] +## - Progress reporting from test hooks[br] +## - Debug information and logging[br] +## - User notifications and alerts[br] +## [br] +## The message will be processed by the global GdUnit message system and +## may be displayed in the test runner UI, logged to files, or handled +## by other registered message handlers. +## [br] +## [b][u]Example Usage:[/u][/b] +## [codeblock] +## # In a test hook +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Database connection established") +## return GdUnitResult.success() +## +## func shutdown(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Generated test report: report.html") +## return GdUnitResult.success() +## ``` +## [/codeblock] +## [param message] The message text to send through the GdUnit messaging system +func send_message(message: String) -> void: + GdUnitSignals.instance().gdunit_message.emit(message) diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd.uid new file mode 100644 index 00000000..9b80ab4b --- /dev/null +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd.uid @@ -0,0 +1 @@ +uid://b7lfqiofkghpg diff --git a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd similarity index 65% rename from addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd rename to addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd index 093cc50f..6551e085 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd @@ -22,8 +22,6 @@ const RETURN_WARNING = 101 ## Specifies the Node name under which the runner is registered const GDUNIT_RUNNER = "GdUnitRunner" -## The maximum number of report history files to store -const DEFAULT_REPORT_COUNT = 20 ## The current runner configuration @warning_ignore("unused_private_class_variable") @@ -31,6 +29,7 @@ var _runner_config := GdUnitRunnerConfig.new() ## The test suite executor instance var _executor: GdUnitTestSuiteExecutor +var _hooks : GdUnitTestSessionHookService ## Current runner state var _state := READY @@ -38,6 +37,39 @@ var _state := READY ## Current tests to be processed var _test_cases: Array[GdUnitTestCase] = [] + +## Configured report base path (can be set on CI test runner) +var report_base_path: String = GdUnitFileAccess.current_dir() + "reports": + get: + return report_base_path + + +## Current session report path +var report_path: String: + get: + return "%s/%s%d" % [report_base_path, GdUnitConstants.REPORT_DIR_PREFIX, current_report_history_index] + + +## Current report history index, if max_report_history > 1 we scan for the next index over the existing reports +var current_report_history_index: int: + get: + if max_report_history > 1: + return GdUnitFileAccess.find_last_path_index(report_base_path, GdUnitConstants.REPORT_DIR_PREFIX) + 1 + else: + return 1 + + +## Controls how many report historys will be hold +var max_report_history: int = GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT: + get: + return max_report_history + set(value): + max_report_history = value + + +# holds the current test session context +var _test_session: GdUnitTestSession + ## Runner state machine enum { READY, @@ -47,14 +79,14 @@ enum { EXIT } - func _init() -> void: - # minimize scene window checked debug mode if OS.get_cmdline_args().size() == 1: DisplayServer.window_set_title("GdUnit4 Runner (Debug Mode)") else: DisplayServer.window_set_title("GdUnit4 Runner (Release Mode)") - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) + if not Engine.is_embedded_in_editor(): + # minimize scene window checked debug mode + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) # store current runner instance to engine meta data to can be access in as a singleton Engine.set_meta(GDUNIT_RUNNER, self) @@ -80,24 +112,41 @@ func _notification(what: int) -> void: func _process(_delta: float) -> void: match _state: INIT: - init_runner() + await init_runner() RUN: + _hooks = GdUnitTestSessionHookService.instance() + _test_session = GdUnitTestSession.new(_test_cases, report_path) + GdUnitSignals.instance().gdunit_event.emit(GdUnitSessionStart.new()) # process next test suite set_process(false) + var result := await _hooks.execute_startup(_test_session) + if result.is_error(): + push_error(result.error_message()) await _executor.run_and_wait(_test_cases) + result = await _hooks.execute_shutdown(_test_session) + if result.is_error(): + push_error(result.error_message()) _state = STOP set_process(true) + GdUnitSignals.instance().gdunit_event.emit(GdUnitSessionClose.new()) + cleanup_report_history() STOP: _state = EXIT # give the engine small amount time to finish the rpc - _on_gdunit_event(GdUnitStop.new()) await get_tree().create_timer(0.1).timeout await quit(get_exit_code()) ## Used by the inheriting runners to initialize test execution func init_runner() -> void: - pass + await get_tree().process_frame + + +func cleanup_report_history() -> int: + return GdUnitFileAccess.delete_path_index_lower_equals_than( + report_path.get_base_dir(), + GdUnitConstants.REPORT_DIR_PREFIX, + current_report_history_index-1-max_report_history) ## Returns the exit code when the test run is finished.[br] diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd.uid new file mode 100644 index 00000000..ca7b2a4f --- /dev/null +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd.uid @@ -0,0 +1 @@ +uid://bn3pr1ate6jeu diff --git a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd index 931fe846..20325ac1 100644 --- a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd +++ b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd @@ -10,7 +10,7 @@ const DEFAULT_TEMP_TS_GD =""" @warning_ignore('return_value_discarded') # TestSuite generated from - const __source = '${source_resource_path}' + const __source: String = '${source_resource_path}' """ diff --git a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid index c7b90b3f..da506f24 100644 --- a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid +++ b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid @@ -1 +1 @@ -uid://lsedwn0tyae4 +uid://cc2tbunuqchvw diff --git a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid index ec5aea92..05ed9b96 100644 --- a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid +++ b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid @@ -1 +1 @@ -uid://dj86blx8tmp4e +uid://bmmiu02vli11t diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid index 0b884827..c70b5738 100644 --- a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid +++ b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid @@ -1 +1 @@ -uid://q3vq3k866rdh +uid://rakh8lkeenoq diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid index 04fc5d73..e2854838 100644 --- a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid +++ b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid @@ -1 +1 @@ -uid://n784355nwlfr +uid://tg2r2o128gpq diff --git a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd index 01b7dfea..be5d1e5b 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd +++ b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd @@ -20,6 +20,7 @@ enum { const CSI_BOLD = "" const CSI_ITALIC = "" const CSI_UNDERLINE = "" +const CSI_RESET = "" # Control Sequence Introducer var _debug_show_color_codes := false @@ -28,6 +29,9 @@ var _color_mode := COLOR_TABLE ## Current cursor position in the line var _current_pos := 0 +# Pre-compiled regex patterns for tag matching +var _tag_regex: RegEx + ## Constructs CSI style codes based on flags.[br] ## [br] @@ -44,12 +48,110 @@ func _apply_style_flags(flags: int) -> String: return _style +## Converts a color string (named or hex) to a Color object +func _parse_color(color_str: String) -> Color: + return Color.from_string(color_str.strip_edges().to_lower(), Color.WHITE) + + +## Generates CSI color code for foreground color +func _color_to_csi_fg(c: Color) -> String: + return "[38;2;%d;%d;%dm" % [c.r8 * c.a, c.g8 * c.a, c.b8 * c.a] + + +## Generates CSI color code for background color +func _color_to_csi_bg(c: Color) -> String: + return "[48;2;%d;%d;%dm" % [c.r8 * c.a, c.g8 * c.a, c.b8 * c.a] + + +func _init_regex_patterns() -> void: + if not _tag_regex: + _tag_regex = RegEx.new() + # Match all richtext tags: [tag], [tag=value], [/tag] + _tag_regex.compile(r"\[/?(?:color|bgcolor|b|i|u)(?:=[^\]]+)?\]") + + +func _extract_color_from_tag(tag: String, tag_assign: String) -> Color: + var tag_assign_length := tag_assign.length() + var color_value := tag.substr(tag_assign_length, tag.length() - tag_assign_length - 1) + return _parse_color(color_value) + + +## Optimized richtext to CSI conversion using regex and lookup processing +func _bbcode_tags_to_csi_codes(message: String) -> String: + _init_regex_patterns() + + var result := "" + var last_pos := 0 + var color_stack: Array[Color] = [] + var bgcolor_stack: Array[Color] = [] + + # Find all richtext tags + var matches := _tag_regex.search_all(message) + + for match in matches: + var start_pos := match.get_start() + var end_pos := match.get_end() + var tag := match.get_string(0) + + # Add text before this tag + result += message.substr(last_pos, start_pos - last_pos) + + # Process the tag + if tag.begins_with("[color="): + var fg_color := _extract_color_from_tag(tag, "[color=") + color_stack.push_back(fg_color) + result += _color_to_csi_fg(fg_color) + elif tag.begins_with("[bgcolor="): + var bg_color := _extract_color_from_tag(tag, "[bgcolor=") + bgcolor_stack.push_back(bg_color) + result += _color_to_csi_bg(bg_color) + elif tag == "[b]": + result += CSI_BOLD + elif tag == "[i]": + result += CSI_ITALIC + elif tag == "[u]": + result += CSI_UNDERLINE + elif tag == "[/color]": + result += CSI_RESET + if color_stack.size() > 0: + color_stack.pop_back() + # Restore remaining styles and colors + if color_stack.size() > 0: + result += _color_to_csi_fg(color_stack[-1]) + if bgcolor_stack.size() > 0: + result += _color_to_csi_bg(bgcolor_stack[-1]) + elif tag == "[/bgcolor]": + result += CSI_RESET + if bgcolor_stack.size() > 0: + bgcolor_stack.pop_back() + # Restore remaining styles and colors + if color_stack.size() > 0: + result += _color_to_csi_fg(color_stack[-1]) + if bgcolor_stack.size() > 0: + result += _color_to_csi_bg(bgcolor_stack[-1]) + elif tag in ["[/b]", "[/i]", "[/u]"]: + result += CSI_RESET + # Restore remaining colors after style reset + if color_stack.size() > 0: + result += _color_to_csi_fg(color_stack[-1]) + if bgcolor_stack.size() > 0: + result += _color_to_csi_bg(bgcolor_stack[-1]) + + last_pos = end_pos + + # Add remaining text after last tag + result += message.substr(last_pos) + + return result + + ## Implementation of basic message output with formatting. func _print_message(_message: String, _color: Color, _indent: int, _flags: int) -> void: + var text := _bbcode_tags_to_csi_codes(_message) var indent_text := "".lpad(_indent * 2) var _style := _apply_style_flags(_flags) - printraw("%s[38;2;%d;%d;%dm%s%s" % [indent_text, _color.r8, _color.g8, _color.b8, _style, _message] ) - _current_pos += _indent * 2 + _message.length() + printraw("%s[38;2;%d;%d;%dm%s%s" % [indent_text, _color.r8, _color.g8, _color.b8, _style, text] ) + _current_pos += _indent * 2 + text.length() ## Implementation of line-ending message output with formatting. diff --git a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid index 91139adb..60074986 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid +++ b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid @@ -1 +1 @@ -uid://dl5wgo0612g2i +uid://c3xjxlb3xkafg diff --git a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid index 5abf8a51..6ab67d17 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid +++ b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid @@ -1 +1 @@ -uid://br67vb7sg3vlv +uid://dinagbq0w2sy2 diff --git a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid index 7d26ae5e..c58dd0b9 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid +++ b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid @@ -1 +1 @@ -uid://onxhrb1vx1vm +uid://sward62ppjdu diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs index d62d3da3..14a1355b 100644 --- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs +++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs @@ -1,8 +1,13 @@ +// Copyright (c) 2025 Mike Schulze +// MIT License - See LICENSE file in the repository root for full license text +#pragma warning disable IDE1006 namespace gdUnit4.addons.gdUnit4.src.dotnet; +#pragma warning restore IDE1006 #if GDUNIT4NET_API_V5 using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -13,134 +18,169 @@ namespace gdUnit4.addons.gdUnit4.src.dotnet; using Godot; using Godot.Collections; -// GdUnit4 GDScript - C# API wrapper -// ReSharper disable once CheckNamespace +/// +/// The GdUnit4 GDScript - C# API wrapper. +/// public partial class GdUnit4CSharpApi : GdUnit4NetApiGodotBridge { - [Signal] - public delegate void ExecutionCompletedEventHandler(); - - private CancellationTokenSource? executionCts; - - public override void _Notification(int what) - { - if (what != NotificationPredelete) - return; - executionCts?.Dispose(); - executionCts = null; - } - - public static bool IsApiLoaded() - => true; - - public static Array DiscoverTests(CSharpScript sourceScript) - { - try - { - // Get the list of test case descriptors from the API - var testCaseDescriptors = DiscoverTestsFromScript(sourceScript); - // Convert each TestCaseDescriptor to a Dictionary - return testCaseDescriptors - .Select(descriptor => new Dictionary - { - ["guid"] = descriptor.Id.ToString(), - ["managed_type"] = descriptor.ManagedType, - ["test_name"] = descriptor.ManagedMethod, - ["source_file"] = sourceScript.ResourcePath, - ["line_number"] = descriptor.LineNumber, - ["attribute_index"] = descriptor.AttributeIndex, - ["require_godot_runtime"] = descriptor.RequireRunningGodotEngine, - ["code_file_path"] = descriptor.CodeFilePath ?? "", - ["simple_name"] = descriptor.SimpleName, - ["fully_qualified_name"] = descriptor.FullyQualifiedName, - ["assembly_location"] = descriptor.AssemblyPath - }) - .Aggregate(new Array(), (array, dict) => - { - array.Add(dict); - return array; - }); - } - catch (Exception e) - { - GD.PrintErr($"Error discovering tests: {e.Message}\n{e.StackTrace}"); - return new Array(); - } - } - - public void ExecuteAsync(Array tests, Callable listener) - { - try - { - // Cancel any ongoing execution - executionCts?.Cancel(); - executionCts?.Dispose(); - - // Create new cancellation token source - executionCts = new CancellationTokenSource(); - - var testSuiteNodes = new List { BuildTestSuiteNodeFrom(tests) }; - ExecuteAsync(testSuiteNodes, listener, executionCts.Token) - .GetAwaiter() - .OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted)); - } - catch (Exception e) - { - GD.PrintErr($"Error executing tests: {e.Message}\n{e.StackTrace}"); - Task.Run(() => { }).GetAwaiter().OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted)); - } - } - - public void CancelExecution() - { - try - { - executionCts?.Cancel(); - } - catch (Exception e) - { - GD.PrintErr($"Error cancelling execution: {e.Message}"); - } - } - - // Convert a set of Tests stored as Dictionaries to TestSuiteNode - // all tests are assigned to a single test suit - internal static TestSuiteNode BuildTestSuiteNodeFrom(Array tests) - { - if (tests.Count == 0) - throw new InvalidOperationException("Cant build 'TestSuiteNode' from an empty test set."); - - // Create a suite ID - var suiteId = Guid.NewGuid(); - var firstTest = tests[0]; - var managedType = firstTest["managed_type"].AsString(); - var assemblyLocation = firstTest["assembly_location"].AsString(); - var sourceFile = firstTest["source_file"].AsString(); - - // Create TestCaseNodes for each test in the suite - var testCaseNodes = tests - .Select(test => new TestCaseNode - { - Id = Guid.Parse(test["guid"].AsString()), - ParentId = suiteId, - ManagedMethod = test["test_name"].AsString(), - LineNumber = test["line_number"].AsInt32(), - AttributeIndex = test["attribute_index"].AsInt32(), - RequireRunningGodotEngine = test["require_godot_runtime"].AsBool() - } - ) - .ToList(); - - return new TestSuiteNode - { - Id = suiteId, - ParentId = Guid.Empty, - ManagedType = managedType, - AssemblyPath = assemblyLocation, - SourceFile = sourceFile, - Tests = testCaseNodes - }; - } + /// + /// The signal to be emitted when the execution is completed. + /// + [Signal] +#pragma warning disable CA1711 + public delegate void ExecutionCompletedEventHandler(); +#pragma warning restore CA1711 + +#pragma warning disable CA2213, SA1201 + private CancellationTokenSource? executionCts; +#pragma warning restore CA2213, SA1201 + + /// + /// Indicates if the API loaded. + /// + /// Returns true if the API already loaded. + public static bool IsApiLoaded() + => true; + + /// + /// Runs test discovery on the given script. + /// + /// The script to be scanned. + /// The list of tests discovered as dictionary. + public static Array DiscoverTests(CSharpScript sourceScript) + { + try + { + // Get the list of test case descriptors from the API + var testCaseDescriptors = DiscoverTestsFromScript(sourceScript); + + // Convert each TestCaseDescriptor to a Dictionary + return testCaseDescriptors + .Select(descriptor => new Dictionary + { + ["guid"] = descriptor.Id.ToString(), + ["managed_type"] = descriptor.ManagedType, + ["test_name"] = descriptor.ManagedMethod, + ["source_file"] = sourceScript.ResourcePath, + ["line_number"] = descriptor.LineNumber, + ["attribute_index"] = descriptor.AttributeIndex, + ["require_godot_runtime"] = descriptor.RequireRunningGodotEngine, + ["code_file_path"] = descriptor.CodeFilePath ?? string.Empty, + ["simple_name"] = descriptor.SimpleName, + ["fully_qualified_name"] = descriptor.FullyQualifiedName, + ["assembly_location"] = descriptor.AssemblyPath + }) + .Aggregate(new Array(), (array, dict) => + { + array.Add(dict); + return array; + }); + } +#pragma warning disable CA1031 + catch (Exception e) +#pragma warning restore CA1031 + { + GD.PrintErr($"Error discovering tests: {e.Message}\n{e.StackTrace}"); +#pragma warning disable IDE0028 // Do not catch general exception types + return new Array(); +#pragma warning restore IDE0028 // Do not catch general exception types + } + } + + /// + public override void _Notification(int what) + { + if (what != NotificationPredelete) + return; + executionCts?.Dispose(); + executionCts = null; + } + + /// + /// Executes the tests and using the listener for reporting the results. + /// + /// A list of tests to be executed. + /// The listener to report the results. + public void ExecuteAsync(Array tests, Callable listener) + { + try + { + // Cancel any ongoing execution + executionCts?.Cancel(); + executionCts?.Dispose(); + + // Create new cancellation token source + executionCts = new CancellationTokenSource(); + + Debug.Assert(tests != null, nameof(tests) + " != null"); + var testSuiteNodes = new List { BuildTestSuiteNodeFrom(tests) }; + ExecuteAsync(testSuiteNodes, listener, executionCts.Token) + .GetAwaiter() + .OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted)); + } +#pragma warning disable CA1031 + catch (Exception e) +#pragma warning restore CA1031 + { + GD.PrintErr($"Error executing tests: {e.Message}\n{e.StackTrace}"); + Task.Run(() => { }).GetAwaiter().OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted)); + } + } + + /// + /// Will cancel the current test execution. + /// + public void CancelExecution() + { + try + { + executionCts?.Cancel(); + } +#pragma warning disable CA1031 + catch (Exception e) +#pragma warning restore CA1031 + { + GD.PrintErr($"Error cancelling execution: {e.Message}"); + } + } + + // Convert a set of Tests stored as Dictionaries to TestSuiteNode + // all tests are assigned to a single test suit + internal static TestSuiteNode BuildTestSuiteNodeFrom(Array tests) + { + if (tests.Count == 0) + throw new InvalidOperationException("Cant build 'TestSuiteNode' from an empty test set."); + + // Create a suite ID + var suiteId = Guid.NewGuid(); + var firstTest = tests[0]; + var managedType = firstTest["managed_type"].AsString(); + var assemblyLocation = firstTest["assembly_location"].AsString(); + var sourceFile = firstTest["source_file"].AsString(); + + // Create TestCaseNodes for each test in the suite + var testCaseNodes = tests + .Select(test => new TestCaseNode + { + Id = Guid.Parse(test["guid"].AsString()), + ParentId = suiteId, + ManagedMethod = test["test_name"].AsString(), + LineNumber = test["line_number"].AsInt32(), + AttributeIndex = test["attribute_index"].AsInt32(), + RequireRunningGodotEngine = test["require_godot_runtime"].AsBool() + }) + .ToList(); + + return new TestSuiteNode + { + Id = suiteId, + ParentId = Guid.Empty, + ManagedType = managedType, + AssemblyPath = assemblyLocation, + SourceFile = sourceFile, + Tests = testCaseNodes + }; + } } #else using Godot; diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd index 199570c6..3d8ba25f 100644 --- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd +++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd @@ -69,7 +69,7 @@ static func is_api_loaded() -> bool: # 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.IsApiLoaded() + return _gdUnit4NetWrapper.call("IsApiLoaded") ## Returns the version of the GdUnit4 .NET assembly.[br] diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid index 325ac7a7..f898f790 100644 --- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid +++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid @@ -1 +1 @@ -uid://df7eum854u24l +uid://budefd2x2uovj diff --git a/addons/gdUnit4/src/doubler/CallableDoubler.gd b/addons/gdUnit4/src/doubler/CallableDoubler.gd index 14a5947d..1bc78a1a 100644 --- a/addons/gdUnit4/src/doubler/CallableDoubler.gd +++ b/addons/gdUnit4/src/doubler/CallableDoubler.gd @@ -58,20 +58,8 @@ static func callable_functions() -> PackedStringArray: ## Callable functions stubing ## ----------------------------------------------------------------------------------------------------------------------------------------- -@warning_ignore("untyped_declaration") -func bind(arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> Callable: - # save - var bind_values: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) - _cb = _cb.bindv(bind_values) +func bind(...varargs: Array) -> Callable: + _cb = _cb.bindv(varargs) return _cb @@ -80,34 +68,19 @@ func bindv(caller_args: Array) -> Callable: return _cb -@warning_ignore("untyped_declaration", "native_method_override", "unused_parameter") -func call(arg0=null, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> Variant: - - # This is a placeholder function signanture without any functionallity! - # It is used by the function doubler to double function signature of Callable:call() - # The doubled function calls direct _cb.callv() see GdUnitSpyFunctionDoubler:TEMPLATE_CALLABLE_CALL template - assert(false) - return null +@warning_ignore("native_method_override") +func call(...varargs: Array) -> Variant: + return _cb.callv(varargs) # Is not supported, see class description -#func call_deferred(a) -> void: -# pass +#func call_deferred(...varargs: Array) -> void: +# return _cb.call_deferred(varargs) # Is not supported, see class description -#func callv(a) -> void: -# pass - +#func callv(arguments: Array) -> Variant: +# return _cb.callv(arguments) func get_bound_arguments() -> Array: @@ -150,60 +123,33 @@ func is_valid() -> bool: return _cb.is_valid() -@warning_ignore("untyped_declaration") -func rpc(arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> void: - - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) - match args.size(): - 0: _cb.rpc(0) - 1: _cb.rpc(args[0]) - 2: _cb.rpc(args[0], args[1]) - 3: _cb.rpc(args[0], args[1], args[2]) - 4: _cb.rpc(args[0], args[1], args[2], args[3]) - 5: _cb.rpc(args[0], args[1], args[2], args[3], args[4]) - 6: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5]) - 7: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6]) - 8: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]) - 9: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]) - 10: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]) +func rpc(...varargs: Array) -> void: + match varargs.size(): + 0: _cb.rpc() + 1: _cb.rpc(varargs[0]) + 2: _cb.rpc(varargs[0], varargs[1]) + 3: _cb.rpc(varargs[0], varargs[1], varargs[2]) + 4: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4]) + 5: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5]) + 6: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6]) + 7: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7]) + 8: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8]) + 9: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8], varargs[9]) @warning_ignore("untyped_declaration") -func rpc_id(peer_id: int, - arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> void: - - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) - match args.size(): - 0: _cb.rpc_id(peer_id) - 1: _cb.rpc_id(peer_id, args[0]) - 2: _cb.rpc_id(peer_id, args[0], args[1]) - 3: _cb.rpc_id(peer_id, args[0], args[1], args[2]) - 4: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3]) - 5: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4]) - 6: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5]) - 7: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6]) - 8: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]) - 9: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]) - 10: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]) - +func rpc_id(peer_id: int, ...varargs: Array) -> void: + match varargs.size(): + 0: _cb.rpc_id(peer_id ) + 1: _cb.rpc_id(peer_id, varargs[0]) + 2: _cb.rpc_id(peer_id, varargs[0], varargs[1]) + 3: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2]) + 4: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4]) + 5: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5]) + 6: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6]) + 7: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7]) + 8: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8]) + 9: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8], varargs[9]) func unbind(argcount: int) -> Callable: _cb = _cb.unbind(argcount) diff --git a/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid b/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid index b01ab9ad..bb882086 100644 --- a/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid +++ b/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid @@ -1 +1 @@ -uid://d1ej1c5t4ohgi +uid://bgb8nvj6dgyot diff --git a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd index 090b71df..d9459dc3 100644 --- a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd +++ b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd @@ -1,226 +1,4 @@ -class_name GdFunctionDoubler +@abstract class_name GdFunctionDoubler extends RefCounted -const DEFAULT_TYPED_RETURN_VALUES := { - TYPE_NIL: "null", - TYPE_BOOL: "false", - TYPE_INT: "0", - TYPE_FLOAT: "0.0", - TYPE_STRING: "\"\"", - TYPE_STRING_NAME: "&\"\"", - TYPE_VECTOR2: "Vector2.ZERO", - TYPE_VECTOR2I: "Vector2i.ZERO", - TYPE_RECT2: "Rect2()", - TYPE_RECT2I: "Rect2i()", - TYPE_VECTOR3: "Vector3.ZERO", - TYPE_VECTOR3I: "Vector3i.ZERO", - TYPE_VECTOR4: "Vector4.ZERO", - TYPE_VECTOR4I: "Vector4i.ZERO", - TYPE_TRANSFORM2D: "Transform2D()", - TYPE_PLANE: "Plane()", - TYPE_QUATERNION: "Quaternion()", - TYPE_AABB: "AABB()", - TYPE_BASIS: "Basis()", - TYPE_TRANSFORM3D: "Transform3D()", - TYPE_PROJECTION: "Projection()", - TYPE_COLOR: "Color()", - TYPE_NODE_PATH: "NodePath()", - TYPE_RID: "RID()", - TYPE_OBJECT: "null", - TYPE_CALLABLE: "Callable()", - TYPE_SIGNAL: "Signal()", - TYPE_DICTIONARY: "Dictionary()", - TYPE_ARRAY: "Array()", - TYPE_PACKED_BYTE_ARRAY: "PackedByteArray()", - TYPE_PACKED_INT32_ARRAY: "PackedInt32Array()", - TYPE_PACKED_INT64_ARRAY: "PackedInt64Array()", - TYPE_PACKED_FLOAT32_ARRAY: "PackedFloat32Array()", - TYPE_PACKED_FLOAT64_ARRAY: "PackedFloat64Array()", - TYPE_PACKED_STRING_ARRAY: "PackedStringArray()", - TYPE_PACKED_VECTOR2_ARRAY: "PackedVector2Array()", - TYPE_PACKED_VECTOR3_ARRAY: "PackedVector3Array()", - # since Godot 4.3.beta1 TYPE_PACKED_VECTOR4_ARRAY = 38 - GdObjects.TYPE_PACKED_VECTOR4_ARRAY: "PackedVector4Array()", - TYPE_PACKED_COLOR_ARRAY: "PackedColorArray()", - GdObjects.TYPE_VARIANT: "null", - GdObjects.TYPE_ENUM: "0" -} - -# @GlobalScript enums -# needs to manually map because of https://github.com/godotengine/godot/issues/73835 -const DEFAULT_ENUM_RETURN_VALUES = { - "Side" : "SIDE_LEFT", - "Corner" : "CORNER_TOP_LEFT", - "Orientation" : "HORIZONTAL", - "ClockDirection" : "CLOCKWISE", - "HorizontalAlignment" : "HORIZONTAL_ALIGNMENT_LEFT", - "VerticalAlignment" : "VERTICAL_ALIGNMENT_TOP", - "InlineAlignment" : "INLINE_ALIGNMENT_TOP_TO", - "EulerOrder" : "EULER_ORDER_XYZ", - "Key" : "KEY_NONE", - "KeyModifierMask" : "KEY_CODE_MASK", - "MouseButton" : "MOUSE_BUTTON_NONE", - "MouseButtonMask" : "MOUSE_BUTTON_MASK_LEFT", - "JoyButton" : "JOY_BUTTON_INVALID", - "JoyAxis" : "JOY_AXIS_INVALID", - "MIDIMessage" : "MIDI_MESSAGE_NONE", - "Error" : "OK", - "PropertyHint" : "PROPERTY_HINT_NONE", - "Variant.Type" : "TYPE_NIL", -} - -var _push_errors :String - - -# Determine the enum default by reflection -static func get_enum_default(value :String) -> Variant: - var script := GDScript.new() - script.source_code = """ - extends Resource - - static func get_enum_default() -> Variant: - return %s.values()[0] - - """.dedent() % value - @warning_ignore("return_value_discarded") - script.reload() - @warning_ignore("unsafe_method_access") - return script.new().call("get_enum_default") - - -static func default_return_value(func_descriptor :GdFunctionDescriptor) -> String: - var return_type :Variant = func_descriptor.return_type() - if return_type == GdObjects.TYPE_ENUM: - var enum_class := func_descriptor._return_class - var enum_path := enum_class.split(".") - if enum_path.size() >= 2: - var keys := ClassDB.class_get_enum_constants(enum_path[0], enum_path[1]) - if not keys.is_empty(): - return "%s.%s" % [enum_path[0], keys[0]] - var enum_value :Variant = get_enum_default(enum_class) - if enum_value != null: - return str(enum_value) - # we need fallback for @GlobalScript enums, - return DEFAULT_ENUM_RETURN_VALUES.get(func_descriptor._return_class, "0") - return DEFAULT_TYPED_RETURN_VALUES.get(return_type, "invalid") - - -func _init(push_errors :bool = false) -> void: - _push_errors = "true" if push_errors else "false" - for type_key in TYPE_MAX: - if not DEFAULT_TYPED_RETURN_VALUES.has(type_key): - push_error("missing default definitions! Expexting %d bud is %d" % [DEFAULT_TYPED_RETURN_VALUES.size(), TYPE_MAX]) - prints("missing default definition for type", type_key) - assert(DEFAULT_TYPED_RETURN_VALUES.has(type_key), "Missing Type default definition!") - - -@warning_ignore("unused_parameter") -func get_template(return_type: GdFunctionDescriptor, is_callable: bool) -> String: - assert(false, "'get_template' must be implemented!") - return "" - - -func double(func_descriptor: GdFunctionDescriptor, is_callable: bool = false) -> PackedStringArray: - var is_coroutine := func_descriptor.is_coroutine() - var func_name := func_descriptor.name() - var args := func_descriptor.args() - var varargs := func_descriptor.varargs() - var return_value := GdFunctionDoubler.default_return_value(func_descriptor) - var arg_names := extract_arg_names(args, true) - var vararg_names := extract_arg_names(varargs) - - # save original constructor arguments - if func_name == "_init": - var constructor_args := ",".join(GdFunctionDoubler.extract_constructor_args(args)) - var constructor := """ - func _init(%s) -> void: - Engine.set_meta(__INSTANCE_ID, self) - @warning_ignore("unsafe_call_argument") - super(%s) - - """.dedent() % [constructor_args, ", ".join(arg_names)] - return constructor.split("\n") - - var double_src := "@warning_ignore('shadowed_variable', 'untyped_declaration')\n" - if func_descriptor.is_engine(): - double_src += '@warning_ignore("native_method_override")\n' - double_src += GdFunctionDoubler.extract_func_signature(func_descriptor) - # fix to unix format, this is need when the template is edited under windows than the template is stored with \r\n - var func_template := get_template(func_descriptor, is_callable).replace("\r\n", "\n") - double_src += func_template\ - .replace("$(arguments)", ", ".join(arg_names))\ - .replace("$(varargs)", ", ".join(vararg_names))\ - .replace("$(await)", GdFunctionDoubler.await_is_coroutine(is_coroutine)) \ - .replace("$(func_name)", func_name )\ - .replace("${default_return_value}", return_value)\ - .replace("$(push_errors)", _push_errors) - - if func_descriptor.return_type() == GdObjects.TYPE_ENUM: - double_src = double_src.replace("$(return_as)", " as " + func_descriptor.return_type_as_string()) - else: - double_src = double_src.replace("$(return_as)", "") - - return double_src.split("\n") - - -func extract_arg_names(argument_signatures: Array[GdFunctionArgument], add_suffix := false) -> PackedStringArray: - var arg_names := PackedStringArray() - for arg in argument_signatures: - @warning_ignore("return_value_discarded") - arg_names.append(arg._name + ("_" if add_suffix else "")) - return arg_names - - -static func extract_constructor_args(args :Array[GdFunctionArgument]) -> PackedStringArray: - var constructor_args := PackedStringArray() - for arg in args: - var arg_name := arg._name + "_" - var default_value := get_default(arg) - if default_value == "null": - @warning_ignore("return_value_discarded") - constructor_args.append(arg_name + ":Variant=" + default_value) - else: - @warning_ignore("return_value_discarded") - constructor_args.append(arg_name + ":=" + default_value) - return constructor_args - - -static func extract_func_signature(descriptor: GdFunctionDescriptor) -> String: - var func_signature := "" - if descriptor._return_type == TYPE_NIL: - func_signature = "func %s(%s) -> void:" % [descriptor.name(), typeless_args(descriptor)] - elif descriptor._return_type == GdObjects.TYPE_VARIANT: - func_signature = "func %s(%s):" % [descriptor.name(), typeless_args(descriptor)] - else: - func_signature = "func %s(%s) -> %s:" % [descriptor.name(), typeless_args(descriptor), descriptor.return_type_as_string()] - return "static " + func_signature if descriptor.is_static() else func_signature - - -static func typeless_args(descriptor: GdFunctionDescriptor) -> String: - var collect := PackedStringArray() - for arg in descriptor.args(): - if arg.has_default(): - # For Variant types we need to enforce the type in the signature - if arg.type() == GdObjects.TYPE_VARIANT: - collect.push_back("%s_:%s=%s" % [arg.name(), GdObjects.type_as_string(arg.type()), arg.value_as_string()]) - else: - @warning_ignore("return_value_discarded") - collect.push_back("%s_=%s" % [arg.name(), arg.value_as_string()]) - else: - @warning_ignore("return_value_discarded") - collect.push_back(arg.name() + "_") - for arg in descriptor.varargs(): - @warning_ignore("return_value_discarded") - collect.push_back(arg.name() + "=" + arg.value_as_string()) - return ", ".join(collect) - - -static func get_default(arg :GdFunctionArgument) -> String: - if arg.has_default(): - return arg.value_as_string() - else: - return DEFAULT_TYPED_RETURN_VALUES.get(arg.type(), "null") - - -static func await_is_coroutine(is_coroutine :bool) -> String: - return "await " if is_coroutine else "" +@abstract func double(func_descriptor: GdFunctionDescriptor) -> PackedStringArray diff --git a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid index ab31c40e..5d4241a7 100644 --- a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid +++ b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid @@ -1 +1 @@ -uid://bch1bvsux2i7r +uid://dqb65tjuhxvlt diff --git a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd index ee02fff8..31aa06bc 100644 --- a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd +++ b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd @@ -2,7 +2,6 @@ class_name GdUnitClassDoubler extends RefCounted - const DOUBLER_INSTANCE_ID_PREFIX := "gdunit_doubler_instance_id_" const EXCLUDE_VIRTUAL_FUNCTIONS = [ # we have to exclude notifications because NOTIFICATION_PREDELETE is try @@ -29,23 +28,23 @@ static func check_leaked_instances() -> void: ## we check that all registered spy/mock instances are removed from the engine meta data for key in Engine.get_meta_list(): if key.begins_with(DOUBLER_INSTANCE_ID_PREFIX): - var instance :Variant = Engine.get_meta(key) + var instance: Variant = Engine.get_meta(key) push_error("GdUnit internal error: an spy/mock instance '%s', class:'%s' is not removed from the engine and will lead in a leaked instance!" % [instance, instance.__SOURCE_CLASS]) + await (Engine.get_main_loop() as SceneTree).process_frame # loads the doubler template # class_info = { "class_name": <>, "class_path" : <>} -static func load_template(template: String, class_info: Dictionary, instance: Object) -> PackedStringArray: - # store instance id +static func load_template(template: String, class_info: Dictionary) -> PackedStringArray: var clazz_name: String = class_info.get("class_name") var source_code := template\ - .replace("${instance_id}", "%s%d" % [DOUBLER_INSTANCE_ID_PREFIX, abs(instance.get_instance_id())])\ .replace("${source_class}", clazz_name)\ # Replace template class_name DoubledClass with source class name .replace("SourceClassName", clazz_name.replace(".", "_")) var lines := GdScriptParser.to_unix_format(source_code).split("\n") @warning_ignore("return_value_discarded") lines.insert(1, extends_clazz(class_info)) + lines.insert(0, "@warning_ignore_start('unsafe_call_argument', 'shadowed_variable', 'untyped_declaration', 'native_method_override', 'int_as_enum_without_cast')") return lines @@ -80,7 +79,7 @@ static func double_functions(instance: Object, clazz_name: String, clazz_path: P continue if functions.has(func_descriptor.name()) or exclude_override_functions.has(func_descriptor.name()): continue - doubled_source += func_doubler.double(func_descriptor, instance is CallableDoubler) + doubled_source += func_doubler.double(func_descriptor) functions.append(func_descriptor.name()) # double regular class functions @@ -101,7 +100,7 @@ static func double_functions(instance: Object, clazz_name: String, clazz_path: P #prints("no virtual func implemented",clazz_name, func_descriptor.name() ) continue functions.append(func_descriptor.name()) - doubled_source.append_array(func_doubler.double(func_descriptor, instance is CallableDoubler)) + doubled_source.append_array(func_doubler.double(func_descriptor)) return doubled_source diff --git a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid index 731bab95..04e7d7dc 100644 --- a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid +++ b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid @@ -1 +1 @@ -uid://c8xblsbebv4at +uid://ck4vbxiyh6m5r diff --git a/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd new file mode 100644 index 00000000..a1a75f11 --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd @@ -0,0 +1,336 @@ +class_name GdUnitFunctionDoublerBuilder +extends RefCounted + +const TYPE_VOID = GdObjects.TYPE_VOID +const TYPE_VARIANT = GdObjects.TYPE_VARIANT +const TYPE_VARARG = GdObjects.TYPE_VARARG +const TYPE_FUNC = GdObjects.TYPE_FUNC +const TYPE_FUZZER = GdObjects.TYPE_FUZZER +const TYPE_ENUM = GdObjects.TYPE_ENUM + +const DEFAULT_TYPED_RETURN_VALUES := { + TYPE_NIL: "null", + TYPE_BOOL: "false", + TYPE_INT: "0", + TYPE_FLOAT: "0.0", + TYPE_STRING: "\"\"", + TYPE_STRING_NAME: "&\"\"", + TYPE_VECTOR2: "Vector2.ZERO", + TYPE_VECTOR2I: "Vector2i.ZERO", + TYPE_RECT2: "Rect2()", + TYPE_RECT2I: "Rect2i()", + TYPE_VECTOR3: "Vector3.ZERO", + TYPE_VECTOR3I: "Vector3i.ZERO", + TYPE_VECTOR4: "Vector4.ZERO", + TYPE_VECTOR4I: "Vector4i.ZERO", + TYPE_TRANSFORM2D: "Transform2D()", + TYPE_PLANE: "Plane()", + TYPE_QUATERNION: "Quaternion()", + TYPE_AABB: "AABB()", + TYPE_BASIS: "Basis()", + TYPE_TRANSFORM3D: "Transform3D()", + TYPE_PROJECTION: "Projection()", + TYPE_COLOR: "Color()", + TYPE_NODE_PATH: "NodePath()", + TYPE_RID: "RID()", + TYPE_OBJECT: "null", + TYPE_CALLABLE: "Callable()", + TYPE_SIGNAL: "Signal()", + TYPE_DICTIONARY: "Dictionary()", + TYPE_ARRAY: "Array()", + TYPE_PACKED_BYTE_ARRAY: "PackedByteArray()", + TYPE_PACKED_INT32_ARRAY: "PackedInt32Array()", + TYPE_PACKED_INT64_ARRAY: "PackedInt64Array()", + TYPE_PACKED_FLOAT32_ARRAY: "PackedFloat32Array()", + TYPE_PACKED_FLOAT64_ARRAY: "PackedFloat64Array()", + TYPE_PACKED_STRING_ARRAY: "PackedStringArray()", + TYPE_PACKED_VECTOR2_ARRAY: "PackedVector2Array()", + TYPE_PACKED_VECTOR3_ARRAY: "PackedVector3Array()", + TYPE_PACKED_VECTOR4_ARRAY: "PackedVector4Array()", + TYPE_PACKED_COLOR_ARRAY: "PackedColorArray()", + GdObjects.TYPE_VARIANT: "null", + GdObjects.TYPE_ENUM: "0" +} + + +# @GlobalScript enums +# needs to manually map because of https://github.com/godotengine/godot/issues/73835 +const DEFAULT_ENUM_RETURN_VALUES = { + "Side" : "SIDE_LEFT", + "Corner" : "CORNER_TOP_LEFT", + "Orientation" : "HORIZONTAL", + "ClockDirection" : "CLOCKWISE", + "HorizontalAlignment" : "HORIZONTAL_ALIGNMENT_LEFT", + "VerticalAlignment" : "VERTICAL_ALIGNMENT_TOP", + "InlineAlignment" : "INLINE_ALIGNMENT_TOP_TO", + "EulerOrder" : "EULER_ORDER_XYZ", + "Key" : "KEY_NONE", + "KeyModifierMask" : "KEY_CODE_MASK", + "MouseButton" : "MOUSE_BUTTON_NONE", + "MouseButtonMask" : "MOUSE_BUTTON_MASK_LEFT", + "JoyButton" : "JOY_BUTTON_INVALID", + "JoyAxis" : "JOY_AXIS_INVALID", + "MIDIMessage" : "MIDI_MESSAGE_NONE", + "Error" : "OK", + "PropertyHint" : "PROPERTY_HINT_NONE", + "Variant.Type" : "TYPE_NIL", + "Vector2.Axis" : "Vector2.AXIS_X", + "Vector2i.Axis" : "Vector2i.AXIS_X", + "Vector3.Axis" : "Vector3.AXIS_X", + "Vector3i.Axis" : "Vector3i.AXIS_X", + "Vector4.Axis" : "Vector4.AXIS_X", + "Vector4i.Axis" : "Vector4i.AXIS_X", +} + + +static var def_constructor := """ + func _init({constructor_args}) -> void: + __init_doubler() + super({args}) + """.dedent() + + +static var def_verify_block := """ + # verify block + var __verifier := __get_verifier() + if __verifier != null: + if __verifier.is_verify_interactions(): + __verifier.verify_interactions("{func_name}", __args) + {default_return} + else: + __verifier.save_function_interaction("{func_name}", __args) + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_prepare_block := """ + if __is_prepare_return_value(): + __save_function_return_value("{func_name}", __args) + {default_return} + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_void_prepare_block := """ + if __is_prepare_return_value(): + push_error("Mocking functions with return type void is not allowed!") + return + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_mock_return := """ + if __is_do_not_call_real_func("{func_name}", __args): + return __return_mock_value("{func_name}", __args, {default_return}) + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_void_mock_return := """ + if __is_do_not_call_real_func("{func_name}", __args): + return + """.dedent().indent("\t").trim_suffix("\n") + + +var fd: GdFunctionDescriptor +var func_args: Array +var default_return: String +var verify_block: String = "" +var prepare_block: String = "" +var mock_return: String = "" + + +func _init(descriptor: GdFunctionDescriptor) -> void: + # verify all default types are covered + for type_key in TYPE_MAX: + if not DEFAULT_TYPED_RETURN_VALUES.has(type_key): + push_error("missing default definitions! Expexting %d bud is %d" % [DEFAULT_TYPED_RETURN_VALUES.size(), TYPE_MAX]) + prints("missing default definition for type", type_key) + assert(DEFAULT_TYPED_RETURN_VALUES.has(type_key), "Missing Type default definition!") + + fd = descriptor + func_args = argument_names() + default_return = default_return_value() + + +func build_func_signature() -> String: + var return_type := ":" if fd._return_type == TYPE_VARIANT else " -> %s:" % fd.return_type_as_string() + return "{static}func {func_name}({args}){return_type}".format({ + "static" : "static " if fd.is_static() else "", + "func_name": fd.name(), + "args": arguments_full_quilified(), + "return_type": return_type + }) + + +func arguments_full_quilified() -> String: + var collect := PackedStringArray() + for arg in fd.args(): + var name := argument_name(arg) + if arg.has_default(): + var signature := "{argument_name}{arg_typed}={arg_value}".format({ + "argument_name" : name, + "arg_typed" : ":"+GdObjects.type_as_string(arg.type()) if arg.type() == GdObjects.TYPE_VARIANT else "", + "arg_value" : arg.value_as_string() + }) + collect.push_back(signature) + else: + collect.push_back(name) + if fd.is_vararg(): + var arg_descriptor := fd.varargs()[0] + collect.push_back("...%s_: Array" % arg_descriptor.name()) + return ", ".join(collect) + + +func argument_name(arg: GdFunctionArgument) -> String: + return arg.name() + "_" + + +func argument_names() -> PackedStringArray: + return fd.args().map(argument_name) + + +func argument_default(arg :GdFunctionArgument) -> String: + return (arg.value_as_string() + if arg.has_default() + else DEFAULT_TYPED_RETURN_VALUES.get(arg.type(), "null")) + + +func build_constructor_arguments() -> String: + var arguments := PackedStringArray() + for arg in fd.args(): + var default_value := argument_default(arg) + var arg_signature := "{name}:{type}={default}".format({ + "name" : argument_name(arg), + "type" : "Variant" if default_value == "null" else "", + "default" : default_value + }) + arguments.append(arg_signature) + if fd.is_vararg(): + arguments.append("...varargs: Array") + return ", ".join(arguments) + + +func build_arguments() -> String: + return "\tvar __args := [{args}]{varargs}".format({ + "args" : ", ".join(func_args), + "varargs" : " + varargs_" if fd.is_vararg() else "" + }) + + +func build_super_calls() -> String: + if !fd.is_vararg(): + return 'super(%s)\n' % ", ".join(func_args) + + var match_block := "match varargs_.size():\n" + for index in range(0, 11): + match_block += '{index}: super({args})\n'.format({ + "index" : index, + "args" : ", ".join(func_args + build_vararg_list(index)) + }).indent("\t") + match_block += '_: push_error("To many varradic arguments.")\n'.indent("\t") + match_block += "return\n" if is_void_func() else "return %s\n" % default_return + return match_block + + +func build_vararg_list(count: int) -> Array: + var arg_list := [] + for index in count: + arg_list.append("varargs_[%d]" % index) + return arg_list + + +func default_return_value() -> String: + var return_type: Variant = fd.return_type() + if return_type == GdObjects.TYPE_ENUM: + var enum_class := fd._return_class + if DEFAULT_ENUM_RETURN_VALUES.has(enum_class): + return DEFAULT_ENUM_RETURN_VALUES.get(fd._return_class, "0") + + var enum_path := enum_class.split(".") + if enum_path.size() >= 2: + var keys := ClassDB.class_get_enum_constants(enum_path[0], enum_path[1]) + if not keys.is_empty(): + return "%s.%s" % [enum_path[0], keys[0]] + var enum_value: Variant = get_enum_default(enum_class) + if enum_value != null: + return str(enum_value) + # we need fallback for @GlobalScript enums, + return DEFAULT_ENUM_RETURN_VALUES.get(fd._return_class, "0") + return DEFAULT_TYPED_RETURN_VALUES.get(return_type, "invalid") + + +# Determine the enum default by reflection +func get_enum_default(value: String) -> Variant: + var script := GDScript.new() + script.source_code = """ + extends RefCounted + + static func get_enum_default() -> Variant: + return %s.values()[0] + + """.dedent() % value + var err := script.reload() + if err != OK: + push_error("Cant get enum values form '%s', %s" % [value, error_string(err)]) + return 0 + @warning_ignore("unsafe_method_access") + return script.new().call("get_enum_default") + + +func is_void_func() -> bool: + return fd.return_type() == TYPE_NIL or fd.return_type() == TYPE_VOID + + +func with_verify_block() -> GdUnitFunctionDoublerBuilder: + verify_block = def_verify_block.format({ + "func_name" : fd.name(), + "default_return" : "return" if is_void_func() else "return " + default_return + }) + return self + + +func with_prepare_block() -> GdUnitFunctionDoublerBuilder: + if fd.return_type() == TYPE_NIL or fd.return_type() == GdObjects.TYPE_VOID: + prepare_block = def_void_prepare_block + return self + + prepare_block = def_prepare_block.format({ + "func_name" : fd.name(), + "default_return" : "return" if is_void_func() else "return " + default_return + }) + return self + + +func with_mocked_return_value() -> GdUnitFunctionDoublerBuilder: + if is_void_func(): + mock_return = def_void_mock_return.format({ + "func_name" : fd.name(), + }) + else: + mock_return = def_mock_return.format({ + "func_name" : fd.name(), + "default_return" : '"no_arg"' if is_void_func() else default_return + }) + return self + + +func build() -> PackedStringArray: + if fd.name() == "_init": + return [def_constructor.format({ + "constructor_args" : build_constructor_arguments(), + "args" : ", ".join(func_args) + })] + + var func_body: PackedStringArray = [] + func_body.append(build_func_signature()) + func_body.append(build_arguments()) + if not prepare_block.is_empty(): + func_body.append(prepare_block) + func_body.append(verify_block) + if not mock_return.is_empty(): + func_body.append(mock_return) + func_body.append("") + var super_calls := build_super_calls() + if not is_void_func(): + super_calls = super_calls.replace("super(", "return super(" ) + if fd.is_coroutine(): + super_calls = super_calls.replace("super(", "await super(" ) + func_body.append(super_calls.indent("\t")) + return func_body diff --git a/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd.uid b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd.uid new file mode 100644 index 00000000..13622959 --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd.uid @@ -0,0 +1 @@ +uid://dj2jqg0bblfqx diff --git a/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd new file mode 100644 index 00000000..d735bc56 --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd @@ -0,0 +1,10 @@ +class_name GdUnitMockFunctionDoubler +extends GdFunctionDoubler + + +func double(func_descriptor: GdFunctionDescriptor) -> PackedStringArray: + return GdUnitFunctionDoublerBuilder.new(func_descriptor)\ + .with_prepare_block()\ + .with_verify_block()\ + .with_mocked_return_value()\ + .build() diff --git a/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd.uid new file mode 100644 index 00000000..15bfcd33 --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd.uid @@ -0,0 +1 @@ +uid://caro1vxiqblba diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid b/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid index 4f4c888a..3e23d12e 100644 --- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid +++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid @@ -1 +1 @@ -uid://bdto4r8ss52bk +uid://bqpv68daeyd7r diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd index d0b17ce1..36aa9783 100644 --- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd +++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd @@ -5,7 +5,8 @@ var saved_interactions := Dictionary() var verified_interactions := Array() -func save_function_interaction(function_args :Array[Variant]) -> void: +func save_function_interaction(func_name: String, args :Array[Variant]) -> void: + var function_args := [func_name] + args var matcher := GdUnitArgumentMatchers.to_matcher(function_args, true) for index in saved_interactions.keys().size(): var key: Variant = saved_interactions.keys()[index] @@ -23,9 +24,10 @@ func do_verify_interactions(interactions_times: int = 1) -> void: expected_interactions = interactions_times -func verify_interactions(function_args: Array[Variant]) -> void: +func verify_interactions(func_name: String, args: Array[Variant]) -> void: var summary := Dictionary() var total_interactions := 0 + var function_args := [func_name] + args var matcher := GdUnitArgumentMatchers.to_matcher(function_args, true) for index in saved_interactions.keys().size(): var key: Variant = saved_interactions.keys()[index] @@ -80,13 +82,3 @@ func verify_no_more_interactions() -> Dictionary: func reset_interactions() -> void: saved_interactions.clear() - - -func filter_vargs(arg_values: Array[Variant]) -> Array[Variant]: - var filtered: Array[Variant] = [] - for index in arg_values.size(): - var arg: Variant = arg_values[index] - if typeof(arg) == TYPE_STRING and arg == GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE: - continue - filtered.append(arg) - return filtered diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid index 8abb3e79..164e0c94 100644 --- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid +++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid @@ -1 +1 @@ -uid://tmb67mlebh0p +uid://70aymtavn3hb diff --git a/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd new file mode 100644 index 00000000..091241da --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd @@ -0,0 +1,8 @@ +class_name GdUnitSpyFunctionDoubler +extends GdFunctionDoubler + + +func double(func_descriptor: GdFunctionDescriptor) -> PackedStringArray: + return GdUnitFunctionDoublerBuilder.new(func_descriptor)\ + .with_verify_block()\ + .build() diff --git a/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd.uid new file mode 100644 index 00000000..bd32098c --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd.uid @@ -0,0 +1 @@ +uid://dueg022ogfyhp diff --git a/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid b/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid index dbc8f580..9c5b0a58 100644 --- a/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid +++ b/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid @@ -1 +1 @@ -uid://csnbgpgulp7iu +uid://wve1unb72ih8 diff --git a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid index e8663e3f..02f22835 100644 --- a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid @@ -1 +1 @@ -uid://temwde64wr0n +uid://dwonouech3hhx diff --git a/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid index b7af5cb0..a4c69c84 100644 --- a/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid @@ -1 +1 @@ -uid://bdg4hp6nmlgcr +uid://c7ib2xipsmtx4 diff --git a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid index 2a83aca3..d320b7f8 100644 --- a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid @@ -1 +1 @@ -uid://blp2gpwgeemn0 +uid://c72exq1d41ur1 diff --git a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid index 880b0dc5..2e69106f 100644 --- a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid @@ -1 +1 @@ -uid://s8p68klqgfpr +uid://ccray5x2h7hsf diff --git a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid index 0214e17f..56d5f725 100644 --- a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid @@ -1 +1 @@ -uid://bmh5qcuevlvyg +uid://dd5nad8nx5i4k diff --git a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid index 05d4e9b4..3e651159 100644 --- a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid @@ -1 +1 @@ -uid://kbu7v7dsnkwr +uid://c3uqgmmhhrq75 diff --git a/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid index e6e56fd6..d1e160e6 100644 --- a/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://cvdvjs64dx132 +uid://0t41iqykm80n diff --git a/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid index 507e5b16..50acb658 100644 --- a/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://4esn6pmsechf +uid://5okx7vc6e2oi diff --git a/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid index 666ce4d5..e884f38e 100644 --- a/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://0e3ph32ovmeq +uid://p68su2i1aavw diff --git a/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid index fbe93c4c..fb08f62a 100644 --- a/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://btvup13kyyxr2 +uid://b6y02fta06woa diff --git a/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid index acbf6bee..29ce94e0 100644 --- a/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://ev605m3yslq4 +uid://coe52vheexv8f diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd index aa43b80f..d5adc4bb 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd @@ -4,5 +4,10 @@ extends RefCounted @warning_ignore("unused_parameter") -func is_match(value :Variant) -> bool: +func is_match(value: Variant) -> bool: return true + + +func _to_string() -> String: + assert(false, "`_to_string()` Is not implemented!") + return "" diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid index ba93dec3..4d56be28 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://b50scr5ei2ocd +uid://cxofoassm7nop diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd index a40eacee..6a70cc15 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd @@ -4,9 +4,9 @@ extends RefCounted const TYPE_ANY = TYPE_MAX + 100 -static func to_matcher(arguments :Array[Variant], auto_deep_check_mode := false) -> ChainedArgumentMatcher: - var matchers :Array[Variant] = [] - for arg :Variant in arguments: +static func to_matcher(arguments: Array[Variant], auto_deep_check_mode := false) -> ChainedArgumentMatcher: + var matchers: Array[Variant] = [] + for arg: Variant in arguments: # argument is already a matcher if arg is GdUnitArgumentMatcher: matchers.append(arg) @@ -20,13 +20,23 @@ static func any() -> GdUnitArgumentMatcher: return AnyArgumentMatcher.new() -static func by_type(type :int) -> GdUnitArgumentMatcher: +static func by_type(type: int) -> GdUnitArgumentMatcher: return AnyBuildInTypeArgumentMatcher.new([type]) -static func by_types(types :PackedInt32Array) -> GdUnitArgumentMatcher: +static func by_types(types: PackedInt32Array) -> GdUnitArgumentMatcher: return AnyBuildInTypeArgumentMatcher.new(types) -static func any_class(clazz :Object) -> GdUnitArgumentMatcher: +static func any_class(clazz: Object) -> GdUnitArgumentMatcher: return AnyClazzArgumentMatcher.new(clazz) + + +static func is_variant_string_matching(value: Variant) -> GdUnitResult: + if value is String or value is StringName: + return GdUnitResult.success() + if value is GdUnitArgumentMatcher: + if str(value) == "any()" or str(value) == "any_string()": + return GdUnitResult.success() + return GdUnitResult.error("Only 'any()' and 'any_string()' argument matchers are allowed!") + return GdUnitResult.error("Only String or StringName types are allowed!") diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid index 357952e2..55aeb90a 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid @@ -1 +1 @@ -uid://st36iywrw0k5 +uid://cau1m4q6amtt0 diff --git a/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid index a94cd50e..4df0afda 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid +++ b/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid @@ -1 +1 @@ -uid://by7v1svk8t7lb +uid://c8rgj15ec72v8 diff --git a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd index beafb52f..537e9239 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd +++ b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd @@ -22,6 +22,8 @@ static func build(clazz :Variant, mock_mode :String, debug_write := false) -> Va return mock_on_scene(packed_scene, debug_write) # mocking a script var instance := create_instance(clazz) + if instance == null: + push_error("Can't create instance of class %s" % clazz) var mock := mock_on_script(instance, clazz, [ "get_script"], debug_write) if not instance is RefCounted: instance.free() @@ -93,14 +95,20 @@ static func get_class_info(clazz :Variant) -> Dictionary: static func mock_on_script(instance :Object, clazz :Variant, function_excludes :PackedStringArray, debug_write :bool) -> GDScript: - var push_errors := is_push_errors() - var function_doubler := GdUnitMockFunctionDoubler.new(push_errors) + var function_doubler := GdUnitMockFunctionDoubler.new() var class_info := get_class_info(clazz) - var lines := load_template(MOCK_TEMPLATE.source_code, class_info, instance) - var clazz_name :String = class_info.get("class_name") var clazz_path :PackedStringArray = class_info.get("class_path", [clazz_name]) + var mock_template := MOCK_TEMPLATE.source_code.format({ + "instance_id" : abs(instance.get_instance_id()), + "gdunit_source_class": clazz_name if clazz_path.is_empty() else clazz_path[0] + }) + var lines := load_template(mock_template, class_info) lines += double_functions(instance, clazz_name, clazz_path, function_doubler, function_excludes) + # We disable warning/errors for inferred_declaration + if Engine.get_version_info().hex >= 0x40400: + lines.insert(0, '@warning_ignore_start("inferred_declaration")') + lines.append('@warning_ignore_restore("inferred_declaration")') var mock := GDScript.new() mock.source_code = "\n".join(lines) diff --git a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid index b848359b..b04f3531 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid +++ b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid @@ -1 +1 @@ -uid://dvknj4sax1j53 +uid://dutw6jgalmnww diff --git a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd deleted file mode 100644 index 4bbecc30..00000000 --- a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd +++ /dev/null @@ -1,113 +0,0 @@ -class_name GdUnitMockFunctionDoubler -extends GdFunctionDoubler - - -const TEMPLATE_FUNC_WITH_RETURN_VALUE = """ - var args__: Array = ["$(func_name)", $(arguments)] - - if __is_prepare_return_value(): - __save_function_return_value(args__) - return ${default_return_value}$(return_as) - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)", args__): - @warning_ignore("unsafe_call_argument") - return $(await)super($(arguments))$(return_as) - - return __get_mocked_return_value_or_default(args__, ${default_return_value}) - -""" - - -const TEMPLATE_FUNC_WITH_RETURN_VOID = """ - var args__: Array = ["$(func_name)", $(arguments)] - - if __is_prepare_return_value(): - if $(push_errors): - push_error(\"Mocking a void function '$(func_name)() -> void:' is not allowed.\") - return - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)"): - @warning_ignore("unsafe_call_argument") - $(await)super($(arguments)) - -""" - - -const TEMPLATE_FUNC_VARARG_RETURN_VALUE = """ - var varargs__: Array = __get_verifier().filter_vargs([$(varargs)]) - var args__: Array = ["$(func_name)", $(arguments)] + varargs__ - - if __is_prepare_return_value(): - if $(push_errors): - push_error(\"Mocking a void function '$(func_name)() -> void:' is not allowed.\") - __save_function_return_value(args__) - return ${default_return_value}$(return_as) - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)", args__): - match varargs__.size(): - @warning_ignore("unsafe_call_argument") - 0: return $(await)super($(arguments)) - @warning_ignore("unsafe_call_argument") - 1: return $(await)super($(arguments), varargs__[0]) - @warning_ignore("unsafe_call_argument") - 2: return $(await)super($(arguments), varargs__[0], varargs__[1]) - @warning_ignore("unsafe_call_argument") - 3: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2]) - @warning_ignore("unsafe_call_argument") - 4: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3]) - @warning_ignore("unsafe_call_argument") - 5: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4]) - @warning_ignore("unsafe_call_argument") - 6: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5]) - @warning_ignore("unsafe_call_argument") - 7: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6]) - @warning_ignore("unsafe_call_argument") - 8: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7]) - @warning_ignore("unsafe_call_argument") - 9: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7], varargs__[8]) - @warning_ignore("unsafe_call_argument") - 10: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7], varargs__[8], varargs__[9]) - - return __get_mocked_return_value_or_default(args__, ${default_return_value}) - -""" - - -func _init(push_errors :bool = false) -> void: - super._init(push_errors) - - -func get_template(fd: GdFunctionDescriptor, _is_callable: bool) -> String: - if fd.is_vararg(): - return TEMPLATE_FUNC_VARARG_RETURN_VALUE - var return_type :Variant = fd.return_type() - if return_type is StringName: - return TEMPLATE_FUNC_WITH_RETURN_VALUE - return TEMPLATE_FUNC_WITH_RETURN_VOID if (return_type == TYPE_NIL or return_type == GdObjects.TYPE_VOID) else TEMPLATE_FUNC_WITH_RETURN_VALUE diff --git a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid deleted file mode 100644 index ac572715..00000000 --- a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://iw6marcf4cho diff --git a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd index 8c63af04..774ca3e3 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd +++ b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd @@ -3,69 +3,56 @@ class_name DoubledMockClassSourceClassName ################################################################################ # internal mocking stuff ################################################################################ -const __INSTANCE_ID = "${instance_id}" -const __SOURCE_CLASS = "${source_class}" +const __INSTANCE_ID := "gdunit_doubler_instance_id_{instance_id}" -class MockingState: + +class GdUnitMockDoublerState: + const __SOURCE_CLASS := "{gdunit_source_class}" + + var excluded_methods := PackedStringArray() var working_mode := GdUnitMock.RETURN_DEFAULTS - var excluded_methods: PackedStringArray = [] - var return_value: Variant = null var is_prepare_return := false + var return_values := Dictionary() + var return_value: Variant = null - #{ = { - # = - # } - #} - var return_values := Dictionary(): - get: - return return_values + func _init(working_mode_ := GdUnitMock.RETURN_DEFAULTS) -> void: + working_mode = working_mode_ + +var __mock_state := GdUnitMockDoublerState.new() @warning_ignore("unused_private_class_variable") var __verifier_instance := GdUnitObjectInteractionsVerifier.new() -var __mocking_state := MockingState.new() func __init(__script: GDScript, mock_working_mode: String) -> void: - # store self need to access static functions - Engine.set_meta(__INSTANCE_ID, self) super.set_script(__script) - __mocking_state.working_mode = mock_working_mode + __init_doubler() + __mock_state.working_mode = mock_working_mode -func __release_double() -> void: - # we need to release the self reference manually to prevent orphan nodes - Engine.remove_meta(__INSTANCE_ID) +static func __doubler_state() -> GdUnitMockDoublerState: + if Engine.has_meta(__INSTANCE_ID): + return Engine.get_meta(__INSTANCE_ID).__mock_state + return null -func _notification(what: int) -> void: - if what == NOTIFICATION_PREDELETE: - if Engine.has_meta(__INSTANCE_ID): - Engine.remove_meta(__INSTANCE_ID) +func __init_doubler() -> void: + Engine.set_meta(__INSTANCE_ID, self) -static func __mock_state() -> MockingState: - @warning_ignore("unsafe_property_access") - return __get_instance().__mocking_state +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE and Engine.has_meta(__INSTANCE_ID): + Engine.remove_meta(__INSTANCE_ID) static func __get_verifier() -> GdUnitObjectInteractionsVerifier: - var __instance := __get_instance() - @warning_ignore("unsafe_property_access") - return null if __instance == null else __instance.__verifier_instance - - -static func __get_instance() -> Object: - return null if not Engine.has_meta(__INSTANCE_ID) else Engine.get_meta(__INSTANCE_ID) - - -func __instance_id() -> String: - return __INSTANCE_ID + return Engine.get_meta(__INSTANCE_ID).__verifier_instance static func __is_prepare_return_value() -> bool: - return __mock_state().is_prepare_return + return __doubler_state().is_prepare_return static func __sort_by_argument_matcher(__left_args: Array, __right_args: Array) -> bool: @@ -78,7 +65,6 @@ static func __sort_by_argument_matcher(__left_args: Array, __right_args: Array) # we need to sort by matcher arguments so that they are all at the end of the list static func __sort_dictionary(__unsorted_args: Dictionary) -> Dictionary: - var __instance := __get_instance() # only need to sort if contains more than one entry if __unsorted_args.size() <= 1: return __unsorted_args @@ -91,16 +77,14 @@ static func __sort_dictionary(__unsorted_args: Dictionary) -> Dictionary: return __sorted_result -static func __save_function_return_value(__fuction_args: Array) -> void: - var __mock := __mock_state() - var __func_name: String = __fuction_args[0] - var __func_args: Array = __fuction_args.slice(1) - var mocked_return_value_by_args: Dictionary = __mock.return_values.get(__func_name, {}) +static func __save_function_return_value(__func_name: String, __func_args: Array) -> void: + var doubler_state := __doubler_state() + var mocked_return_value_by_args: Dictionary = doubler_state.return_values.get(__func_name, {}) - mocked_return_value_by_args[__func_args] = __mock.return_value - __mock.return_values[__func_name] = __sort_dictionary(mocked_return_value_by_args) - __mock.return_value = null - __mock.is_prepare_return = false + mocked_return_value_by_args[__func_args] = doubler_state.return_value + doubler_state.return_values[__func_name] = __sort_dictionary(mocked_return_value_by_args) + doubler_state.return_value = null + doubler_state.is_prepare_return = false static func __is_mocked_args_match(__func_args: Array, __mocked_args: Array) -> bool: @@ -125,38 +109,35 @@ static func __is_mocked_args_match(__func_args: Array, __mocked_args: Array) -> return __is_matching -static func __get_mocked_return_value_or_default(__fuction_args: Array, __default_return_value: Variant) -> Variant: - var __mock := __mock_state() - var __func_name: String = __fuction_args[0] - if not __mock.return_values.has(__func_name): +static func __return_mock_value(__func_name: String, __func_args: Array, __default_return_value: Variant) -> Variant: + var doubler_state := __doubler_state() + if not doubler_state.return_values.has(__func_name): return __default_return_value - var __func_args: Array = __fuction_args.slice(1) @warning_ignore("unsafe_method_access") - var __mocked_args: Array = __mock.return_values.get(__func_name).keys() + var __mocked_args: Array = doubler_state.return_values.get(__func_name).keys() for __index in __mocked_args.size(): var __margs: Variant = __mocked_args[__index] if __is_mocked_args_match(__func_args, [__margs]): - return __mock.return_values[__func_name][__margs] + return doubler_state.return_values[__func_name][__margs] return __default_return_value -static func __do_call_real_func(__func_name: String, __func_args := []) -> bool: - var __mock := __mock_state() - var __is_call_real_func: bool = __mock.working_mode == GdUnitMock.CALL_REAL_FUNC and not __mock.excluded_methods.has(__func_name) +static func __is_do_not_call_real_func(__func_name: String, __func_args := []) -> bool: + var doubler_state := __doubler_state() + var __is_call_real_func: bool = doubler_state.working_mode == GdUnitMock.CALL_REAL_FUNC and not doubler_state.excluded_methods.has(__func_name) # do not call real funcions for mocked functions - if __is_call_real_func and __mock.return_values.has(__func_name): - var __fuction_args: Array = __func_args.slice(1) + if __is_call_real_func and doubler_state.return_values.has(__func_name): @warning_ignore("unsafe_method_access") - var __mocked_args: Array = __mock.return_values.get(__func_name).keys() - return not __is_mocked_args_match(__fuction_args, __mocked_args) - return __is_call_real_func + var __mocked_args: Array = doubler_state.return_values.get(__func_name).keys() + return __is_mocked_args_match(__func_args, __mocked_args) + return !__is_call_real_func func __exclude_method_call(exluded_methods: PackedStringArray) -> void: - __mocking_state.excluded_methods.append_array(exluded_methods) + __doubler_state().excluded_methods.append_array(exluded_methods) func __do_return(mock_do_return_value: Variant) -> Object: - __mocking_state.return_value = mock_do_return_value - __mocking_state.is_prepare_return = true + __doubler_state().return_value = mock_do_return_value + __doubler_state().is_prepare_return = true return self diff --git a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid index 8cb83dc2..81b06a7e 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid +++ b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid @@ -1 +1 @@ -uid://dwf31euw2o6sn +uid://c2uvigad4fojw diff --git a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid index 823943a3..cae686fd 100644 --- a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid +++ b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid @@ -1 +1 @@ -uid://bj68e27g5j774 +uid://c4fq6pfvi0rjr diff --git a/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid b/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid index c1b8aa0e..252a14aa 100644 --- a/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid +++ b/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid @@ -1 +1 @@ -uid://dj5klui1fx8xd +uid://b184c05820omm diff --git a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid index da97c6e9..c1a4f50f 100644 --- a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid +++ b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid @@ -1 +1 @@ -uid://bbyx05g4opfvp +uid://cawgawnenthuo diff --git a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid index 772d8932..eb1a8356 100644 --- a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid +++ b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid @@ -1 +1 @@ -uid://dxfil3hggwxh0 +uid://sr18spol812i diff --git a/addons/gdUnit4/src/network/GdUnitServer.gd.uid b/addons/gdUnit4/src/network/GdUnitServer.gd.uid index 2418549c..e8c3286f 100644 --- a/addons/gdUnit4/src/network/GdUnitServer.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitServer.gd.uid @@ -1 +1 @@ -uid://cxcxf8yqd3nxu +uid://fqoblx0lpwys diff --git a/addons/gdUnit4/src/network/GdUnitServer.tscn b/addons/gdUnit4/src/network/GdUnitServer.tscn index 616c1d38..4dbe8c49 100644 --- a/addons/gdUnit4/src/network/GdUnitServer.tscn +++ b/addons/gdUnit4/src/network/GdUnitServer.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://cn5mp3tmi2gb1"] -[ext_resource type="Script" uid="uid://cxcxf8yqd3nxu" path="res://addons/gdUnit4/src/network/GdUnitServer.gd" id="1"] -[ext_resource type="Script" uid="uid://dmrtthoylaxgj" path="res://addons/gdUnit4/src/network/GdUnitTcpServer.gd" id="2"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitServer.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitTcpServer.gd" id="2"] [node name="Control" type="Node"] script = ExtResource("1") diff --git a/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid b/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid index b56e2ce0..d28f841c 100644 --- a/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid @@ -1 +1 @@ -uid://dl6v16d27t1yv +uid://b0fu4nh17r1g diff --git a/addons/gdUnit4/src/network/GdUnitTask.gd.uid b/addons/gdUnit4/src/network/GdUnitTask.gd.uid index 283467ac..2864ae90 100644 --- a/addons/gdUnit4/src/network/GdUnitTask.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTask.gd.uid @@ -1 +1 @@ -uid://bxcncilp2jesg +uid://bsdrfuwmjc5ny diff --git a/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid index c13a9241..3a61dfd0 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid @@ -1 +1 @@ -uid://87va3sa5psi1 +uid://wjrr5lmun5nt diff --git a/addons/gdUnit4/src/network/GdUnitTcpNode.gd b/addons/gdUnit4/src/network/GdUnitTcpNode.gd index b051228b..156c764b 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpNode.gd +++ b/addons/gdUnit4/src/network/GdUnitTcpNode.gd @@ -4,7 +4,7 @@ extends Node func rpc_send(stream: StreamPeerTCP, data: RPC) -> void: var package_buffer := StreamPeerBuffer.new() - var buffer := data.serialize().to_ascii_buffer() + var buffer := data.serialize().to_utf16_buffer() package_buffer.put_u32(0xDEADBEEF) package_buffer.put_u32(buffer.size()) var status_code := package_buffer.put_data(buffer) @@ -59,7 +59,7 @@ func receive_packages(stream: StreamPeerTCP, rpc_cb: Callable = noop) -> Array[R else: data_package = buffer[1] - var json := data_package.get_string_from_ascii() + var json := data_package.get_string_from_utf16() if json.is_empty(): push_warning("json is empty, can't process data") continue diff --git a/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid index b271d97e..ac041777 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid @@ -1 +1 @@ -uid://kuufdtk6tuxj +uid://wg4xrtxqocow diff --git a/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid index 35e4f080..be862993 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid @@ -1 +1 @@ -uid://dmrtthoylaxgj +uid://lowroeexs0b7 diff --git a/addons/gdUnit4/src/network/rpc/RPC.gd.uid b/addons/gdUnit4/src/network/rpc/RPC.gd.uid index d326f836..2e15fddf 100644 --- a/addons/gdUnit4/src/network/rpc/RPC.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPC.gd.uid @@ -1 +1 @@ -uid://64l72g2owfu5 +uid://duqnm034itw8t diff --git a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid index 6045a46e..1b4b6bd1 100644 --- a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid @@ -1 +1 @@ -uid://bd1abp2wtd3d4 +uid://dlco7rynlw35v diff --git a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid index 5257fa97..ee6fce45 100644 --- a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid @@ -1 +1 @@ -uid://dv2ef05r4b5uo +uid://honkv4vbostq diff --git a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid index a1b8c617..eb59fc03 100644 --- a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid @@ -1 +1 @@ -uid://d4hifjnhmef5w +uid://djsags73l5l1j diff --git a/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid index 8883d9de..be99e592 100644 --- a/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid @@ -1 +1 @@ -uid://fad5e21h1sae +uid://de552d2nmbfds diff --git a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid deleted file mode 100644 index 22b817f5..00000000 --- a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bcbmkakfma5sm diff --git a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd b/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd deleted file mode 100644 index 4704050c..00000000 --- a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd +++ /dev/null @@ -1,74 +0,0 @@ -@tool -class_name GdUnitHtmlTestReporter -extends GdUnitTestReporter - -var _report: GdUnitHtmlReport - - -func _init(report_dir: String, max_reports: int) -> void: - _report = GdUnitHtmlReport.new(report_dir, max_reports) - - -func on_gdunit_event(event: GdUnitEvent) -> void: - match event.type(): - GdUnitEvent.INIT: - init_summary() - GdUnitEvent.STOP: - _report.write() - _report.delete_history() - GdUnitEvent.TESTSUITE_BEFORE: - init_statistics() - _report.add_testsuite_report(event.resource_path(), event.suite_name(), event.total_count()) - GdUnitEvent.TESTSUITE_AFTER: - var statistics := build_test_suite_statisitcs(event) - _report.update_testsuite_counters( - event.resource_path(), - error_count(statistics), - failed_count(statistics), - orphan_nodes(statistics), - skipped_count(statistics), - flaky_count(statistics), - event.elapsed_time()) - _report.add_testsuite_reports( - event.resource_path(), - event.reports() - ) - GdUnitEvent.TESTCASE_BEFORE: - var test := find_test_by_id(event.guid()) - _report.add_testcase(test.source_file, test.suite_name, test.display_name) - GdUnitEvent.TESTCASE_AFTER: - update_statistics(event) - var test := find_test_by_id(event.guid()) - _report.set_testcase_counters(test.source_file, - test.display_name, - event.error_count(), - event.failed_count(), - event.orphan_nodes(), - event.is_skipped(), - event.is_flaky(), - event.elapsed_time()) - _report.add_testcase_reports(test.source_file, test.display_name, event.reports()) - - -func report_file() -> String: - return _report.report_file() - - -func error_count(statistics: Dictionary) -> int: - return statistics["error_count"] - - -func failed_count(statistics: Dictionary) -> int: - return statistics["failed_count"] - - -func orphan_nodes(statistics: Dictionary) -> int: - return statistics["orphan_nodes"] - - -func skipped_count(statistics: Dictionary) -> int: - return statistics["skipped_count"] - - -func flaky_count(statistics: Dictionary) -> int: - return statistics["flaky_count"] diff --git a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid deleted file mode 100644 index da31f86f..00000000 --- a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c3ifmig6mt1qc diff --git a/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd new file mode 100644 index 00000000..b55b964f --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd @@ -0,0 +1,202 @@ +class_name GdUnitReportSummary +extends RefCounted + +var _resource_path: String +var _name: String +var _test_count := 0 +var _failure_count := 0 +var _error_count := 0 +var _orphan_count := 0 +var _skipped_count := 0 +var _flaky_count := 0 +var _duration := 0 +var _reports: Array[GdUnitReportSummary] = [] +var _text_formatter: Callable + + +func _init(text_formatter: Callable) -> void: + _text_formatter = text_formatter + + +func name() -> String: + return _name + + +func path() -> String: + return _resource_path.get_base_dir().replace("res://", "") + + +func get_resource_path() -> String: + return _resource_path + + +func suite_count() -> int: + return _reports.size() + + +func suite_executed_count() -> int: + var executed := _reports.size() + for report in _reports: + if report.test_count() == report.skipped_count(): + executed -= 1 + return executed + + +func test_count() -> int: + var count := _test_count + for report in _reports: + count += report.test_count() + return count + + +func test_executed_count() -> int: + return test_count() - skipped_count() + + +func success_count() -> int: + return test_count() - error_count() - failure_count() - flaky_count() - skipped_count() + + +func error_count() -> int: + return _error_count + + +func failure_count() -> int: + return _failure_count + + +func skipped_count() -> int: + return _skipped_count + + +func flaky_count() -> int: + return _flaky_count + + +func orphan_count() -> int: + return _orphan_count + + +func duration() -> int: + return _duration + + +func get_reports() -> Array: + return _reports + + +func add_report(report: GdUnitReportSummary) -> void: + _reports.append(report) + + +func report_state() -> String: + return calculate_state(error_count(), failure_count(), orphan_count(), flaky_count(), skipped_count()) + + +func succes_rate() -> String: + return calculate_succes_rate(test_count(), error_count(), failure_count()) + + +@warning_ignore("shadowed_variable") +func add_testcase(resource_path: String, suite_name: String, test_name: String) -> void: + for report: GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == resource_path: + var test_report := GdUnitTestCaseReport.new(resource_path, suite_name, test_name, _text_formatter) + report.add_or_create_test_report(test_report) + + +func add_reports( + p_resource_path: String, + p_test_name: String, + p_reports: Array[GdUnitReport]) -> void: + + for report:GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report.add_testcase_reports(p_test_name, p_reports) + + +func add_testsuite_report(p_resource_path: String, p_suite_name: String, p_test_count: int) -> void: + _reports.append(GdUnitTestSuiteReport.new(p_resource_path, p_suite_name, p_test_count, _text_formatter)) + + +func add_testsuite_reports( + p_resource_path: String, + p_reports: Array = []) -> void: + + for report:GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report.set_reports(p_reports) + + +func set_counters( + p_resource_path: String, + p_test_name: String, + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_is_skipped: bool, + p_is_flaky: bool, + p_duration: int) -> void: + + for report: GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report.set_testcase_counters(p_test_name, p_error_count, p_failure_count, p_orphan_count, + p_is_skipped, p_is_flaky, p_duration) + + +func update_testsuite_counters( + p_resource_path: String, + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_skipped_count: int, + p_flaky_count: int, + p_duration: int) -> void: + + for report:GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report._update_testsuite_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, p_duration) + _update_summary_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, 0) + + +func _update_summary_counters( + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_skipped_count: int, + p_flaky_count: int, + p_duration: int) -> void: + + _error_count += p_error_count + _failure_count += p_failure_count + _orphan_count += p_orphan_count + _skipped_count += p_skipped_count + _flaky_count += p_flaky_count + _duration += p_duration + + +func calculate_state(p_error_count :int, p_failure_count :int, p_orphan_count :int, p_flaky_count: int, p_skipped_count: int) -> String: + if p_error_count > 0: + return "ERROR" + if p_failure_count > 0: + return "FAILED" + if p_flaky_count > 0: + return "FLAKY" + if p_orphan_count > 0: + return "WARNING" + if p_skipped_count > 0: + return "SKIPPED" + return "PASSED" + + +func calculate_succes_rate(p_test_count :int, p_error_count :int, p_failure_count :int) -> String: + if p_failure_count == 0: + return "100%" + var count := p_test_count-p_failure_count-p_error_count + if count < 0: + return "0%" + return "%d" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + "%" + + +func create_summary(_report_dir :String) -> String: + return "" diff --git a/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd.uid b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd.uid new file mode 100644 index 00000000..071f5cab --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd.uid @@ -0,0 +1 @@ +uid://dm6curk7jwvqr diff --git a/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd new file mode 100644 index 00000000..bf4c96ea --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd @@ -0,0 +1,12 @@ +class_name GdUnitReportWriter +extends RefCounted + + +func write(_report_path: String, _report: GdUnitReportSummary) -> String: + assert(false, "'write' is not implemented!") + return "" + + +func output_format() -> String: + assert(false, "'output_format' is not implemented!") + return "" diff --git a/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd.uid new file mode 100644 index 00000000..9706a695 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd.uid @@ -0,0 +1 @@ +uid://ot0ld0ernf8d diff --git a/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd new file mode 100644 index 00000000..2801696c --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd @@ -0,0 +1,47 @@ +class_name GdUnitTestCaseReport +extends GdUnitReportSummary + + +var _suite_name: String +var _failure_reports: Array[GdUnitReport] = [] + + +func _init(p_resource_path: String, p_suite_name: String, p_test_name: String, text_formatter: Callable) -> void: + _resource_path = p_resource_path + _suite_name = p_suite_name + _name = p_test_name + _text_formatter = text_formatter + + +func suite_name() -> String: + return _suite_name + + +func failure_report() -> String: + var report_message := "" + for report in get_test_reports(): + report_message += _text_formatter.call(str(report)) + "\n" + return report_message + + +func add_testcase_reports(reports: Array[GdUnitReport]) -> void: + _failure_reports.append_array(reports) + + +func set_testcase_counters( + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_is_skipped: bool, + p_is_flaky: bool, + p_duration: int) -> void: + _error_count = p_error_count + _failure_count = p_failure_count + _orphan_count = p_orphan_count + _skipped_count = p_is_skipped + _flaky_count = p_is_flaky as int + _duration = p_duration + + +func get_test_reports() -> Array[GdUnitReport]: + return _failure_reports diff --git a/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd.uid b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd.uid new file mode 100644 index 00000000..da63cd58 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd.uid @@ -0,0 +1 @@ +uid://d127jeeo2lklf diff --git a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd index 0749c140..46b81931 100644 --- a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd +++ b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd @@ -1,15 +1,11 @@ class_name GdUnitTestReporter extends RefCounted -var _guard := GdUnitTestDiscoverGuard.instance() + var _statistics := {} var _summary := {} -func on_gdunit_event(_event: GdUnitEvent) -> void: - push_error("Reporter: 'on_gdunit_event' is not implemented!") - - func init_summary() -> void: _summary["suite_count"] = 0 _summary["total_count"] = 0 @@ -25,18 +21,14 @@ func init_statistics() -> void: _statistics.clear() -func update_statistics(event: GdUnitEvent) -> void: - var test_statisitics: Dictionary = _statistics.get_or_add(event.guid(), { - "error_count" : 0, - "failed_count" : 0, - "skipped_count" : event.is_skipped() as int, - "flaky_count" : 0, - "orphan_nodes" : 0 - }) - test_statisitics["error_count"] = event.is_error() as int - test_statisitics["failed_count"] = event.is_failed() as int - test_statisitics["flaky_count"] = event.is_flaky() as int - test_statisitics["orphan_nodes"] = event.orphan_nodes() +func add_test_statistics(event: GdUnitEvent) -> void: + _statistics[event.guid()] = { + "error_count" : event.error_count(), + "failed_count" : event.failed_count(), + "skipped_count" : event.skipped_count(), + "flaky_count" : event.is_flaky() as int, + "orphan_nodes" : event.orphan_nodes() + } func build_test_suite_statisitcs(event: GdUnitEvent) -> Dictionary: @@ -50,15 +42,15 @@ func build_test_suite_statisitcs(event: GdUnitEvent) -> Dictionary: } _summary["suite_count"] += 1 _summary["total_count"] += _statistics.size() - # Add the suite hook specific counters - _summary["error_count"] += event.error_count() - _summary["failed_count"] += event.failed_count() - _summary["orphan_nodes"] += event.orphan_nodes() + _summary["error_count"] += event.error_count() + _summary["failed_count"] += event.failed_count() + _summary["skipped_count"] += event.skipped_count() + _summary["orphan_nodes"] += event.orphan_nodes() _summary["elapsed_time"] += event.elapsed_time() for key: String in ["error_count", "failed_count", "skipped_count", "flaky_count", "orphan_nodes"]: var value: int = _statistics.values().reduce(get_value.bind(key), 0 ) - statistic[key] = value + statistic[key] += value _summary[key] += value return statistic @@ -68,10 +60,6 @@ func get_value(acc: int, value: Dictionary, key: String) -> int: return acc + value[key] -func find_test_by_id(id: GdUnitGUID) -> GdUnitTestCase: - return _guard.find_test_by_id(id) - - func processed_suite_count() -> int: return _summary["suite_count"] @@ -102,3 +90,23 @@ func total_orphan_count() -> int: func elapsed_time() -> int: return _summary["elapsed_time"] + + +func error_count(statistics: Dictionary) -> int: + return statistics["error_count"] + + +func failed_count(statistics: Dictionary) -> int: + return statistics["failed_count"] + + +func orphan_nodes(statistics: Dictionary) -> int: + return statistics["orphan_nodes"] + + +func skipped_count(statistics: Dictionary) -> int: + return statistics["skipped_count"] + + +func flaky_count(statistics: Dictionary) -> int: + return statistics["flaky_count"] diff --git a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid index b23849b1..c7c39621 100644 --- a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid +++ b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid @@ -1 +1 @@ -uid://bv4prgivl4vap +uid://dp1fyjpxr3v4v diff --git a/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd new file mode 100644 index 00000000..9be06825 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd @@ -0,0 +1,96 @@ +class_name GdUnitTestSuiteReport +extends GdUnitReportSummary + +var _time_stamp: int +var _failure_reports: Array[GdUnitReport] = [] + + +func _init(p_resource_path: String, p_name: String, p_test_count: int, text_formatter: Callable) -> void: + _resource_path = p_resource_path + _name = p_name + _test_count = p_test_count + _time_stamp = Time.get_unix_time_from_system() as int + _text_formatter = text_formatter + + +func failure_report() -> String: + var report_message := "" + for report in _failure_reports: + report_message += _text_formatter.call(str(report)) + return report_message + + +func set_duration(p_duration :int) -> void: + _duration = p_duration + + +func time_stamp() -> int: + return _time_stamp + + +func duration() -> int: + return _duration + + +func set_skipped(skipped :int) -> void: + _skipped_count += skipped + + +func set_orphans(orphans :int) -> void: + _orphan_count = orphans + + +func set_failed(count :int) -> void: + _failure_count += count + + +func set_reports(failure_reports :Array[GdUnitReport]) -> void: + _failure_reports = failure_reports + + +func add_or_create_test_report(test_report: GdUnitTestCaseReport) -> void: + _reports.append(test_report) + + +func _update_testsuite_counters( + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_skipped_count: int, + p_flaky_count: int, + p_duration: int) -> void: + _error_count += p_error_count + _failure_count += p_failure_count + _orphan_count += p_orphan_count + _skipped_count += p_skipped_count + _flaky_count += p_flaky_count + _duration += p_duration + + +func set_testcase_counters( + test_name: String, + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_is_skipped: bool, + p_is_flaky: bool, + p_duration: int) -> void: + if _reports.is_empty(): + return + var test_report: GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: + return report.name() == test_name + ).back() + if test_report: + test_report.set_testcase_counters(p_error_count, p_failure_count, p_orphan_count, p_is_skipped, p_is_flaky, p_duration) + + +func add_testcase_reports(test_name: String, reports: Array[GdUnitReport]) -> void: + if reports.is_empty(): + return + # we lookup to latest matching report because of flaky tests could be retry the tests + # and resultis in multipe report entries with the same name + var test_report: GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: + return report.name() == test_name + ).back() + if test_report: + test_report.add_testcase_reports(reports) diff --git a/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd.uid b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd.uid new file mode 100644 index 00000000..5550bfa4 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd.uid @@ -0,0 +1 @@ +uid://owaf7q5ocxut diff --git a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid b/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid deleted file mode 100644 index e0a7a51e..00000000 --- a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dde85bu8fxico diff --git a/addons/gdUnit4/src/reporters/XmlElement.gd.uid b/addons/gdUnit4/src/reporters/XmlElement.gd.uid deleted file mode 100644 index e251d263..00000000 --- a/addons/gdUnit4/src/reporters/XmlElement.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cgfm1ekaq6nco diff --git a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd similarity index 75% rename from addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd rename to addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd index 18e6cc1a..8b54aac0 100644 --- a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd +++ b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd @@ -1,10 +1,22 @@ @tool class_name GdUnitConsoleTestReporter -extends GdUnitTestReporter -const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") + +var test_session: GdUnitTestSession: + get: + return test_session + set(value): + # disconnect first possible connected listener + if test_session != null: + test_session.test_event.disconnect(on_gdunit_event) + # add listening to current session + test_session = value + if test_session != null: + test_session.test_event.connect(on_gdunit_event) + var _writer: GdUnitMessageWritter +var _reporter: GdUnitTestReporter = GdUnitTestReporter.new() var _status_indent := 86 var _detailed: bool var _text_color: Color = Color.ANTIQUE_WHITE @@ -12,7 +24,6 @@ var _function_color: Color = Color.ANTIQUE_WHITE var _engine_type_color: Color = Color.ANTIQUE_WHITE - func _init(writer: GdUnitMessageWritter, detailed := false) -> void: _writer = writer _writer.clear() @@ -37,18 +48,18 @@ func clear() -> void: func on_gdunit_event(event: GdUnitEvent) -> void: match event.type(): GdUnitEvent.INIT: - init_summary() + _reporter.init_summary() GdUnitEvent.STOP: _print_summary() println_message(build_executed_test_suite_msg(processed_suite_count(), processed_suite_count()), Color.DARK_SALMON) - println_message(build_executed_test_case_msg(total_test_count(), total_flaky_count()), Color.DARK_SALMON) + println_message(build_executed_test_case_msg(total_test_count(), total_skipped_count()), Color.DARK_SALMON) println_message("Total execution time: %s" % LocalTime.elapsed(elapsed_time()), Color.DARK_SALMON) # We need finally to set the wave effect to enable the animations _writer.effect(GdUnitMessageWritter.Effect.WAVE).print_at("", 0) GdUnitEvent.TESTSUITE_BEFORE: - init_statistics() + _reporter.init_statistics() print_message("Run Test Suite: ", Color.DARK_TURQUOISE) println_message(event.resource_path(), _engine_type_color) @@ -58,23 +69,23 @@ func on_gdunit_event(event: GdUnitEvent) -> void: .style(GdUnitMessageWritter.BOLD)\ .println_message(event.suite_name()+":finalze") _print_failure_report(event.reports()) - _print_statistics(build_test_suite_statisitcs(event)) + _print_statistics(_reporter.build_test_suite_statisitcs(event)) _print_status(event) println_message("") if _detailed: println_message("") GdUnitEvent.TESTCASE_BEFORE: - var test := find_test_by_id(event.guid()) + var test := test_session.find_test_by_id(event.guid()) _print_test_path(test, event.guid()) if _detailed: _writer.color(Color.FOREST_GREEN).print_at("STARTED", _status_indent) println_message("") GdUnitEvent.TESTCASE_AFTER: - var test := find_test_by_id(event.guid()) - update_statistics(event) + _reporter.add_test_statistics(event) if _detailed: + var test := test_session.find_test_by_id(event.guid()) _print_test_path(test, event.guid()) _print_status(event) _print_failure_report(event.reports()) @@ -131,7 +142,7 @@ func _print_failure_report(reports: Array[GdUnitReport]) -> void: .color(Color.DARK_TURQUOISE)\ .style(GdUnitMessageWritter.BOLD | GdUnitMessageWritter.UNDERLINE)\ .println_message("Report:") - var text := GdUnitTools.richtext_normalize(str(report)) + var text := str(report) for line in text.split("\n", false): _writer.indent(2).color(Color.DARK_TURQUOISE).println_message(line) @@ -141,7 +152,7 @@ func _print_failure_report(reports: Array[GdUnitReport]) -> void: func _print_statistics(statistics: Dictionary) -> void: print_message("Statistics:", Color.DODGER_BLUE) - print_message(" %d tests cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" %\ + print_message(" %d test cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" %\ [statistics["total_count"], statistics["error_count"], statistics["failed_count"], @@ -153,13 +164,13 @@ func _print_statistics(statistics: Dictionary) -> void: func _print_summary() -> void: print_message("Overall Summary:", Color.DODGER_BLUE) _writer\ - .println_message(" %d tests cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" % [ - _summary["total_count"], - _summary["error_count"], - _summary["failed_count"], - _summary["flaky_count"], - _summary["skipped_count"], - _summary["orphan_nodes"] + .println_message(" %d test cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" % [ + total_test_count(), + total_error_count(), + total_failure_count(), + total_flaky_count(), + total_skipped_count(), + total_orphan_count() ]) @@ -169,10 +180,10 @@ func build_executed_test_suite_msg(executed_count: int, total_count: int) -> Str return "Executed test suites: (%d/%d), %d skipped" % [executed_count, total_count, (total_count - executed_count)] -func build_executed_test_case_msg(total_count: int, skipped_count: int) -> String: - if skipped_count == 0: +func build_executed_test_case_msg(total_count: int, p_skipped_count: int) -> String: + if p_skipped_count == 0: return "Executed test cases : (%d/%d)" % [total_count, total_count] - return "Executed test cases : (%d/%d), %d skipped" % [total_count-skipped_count, total_count, skipped_count] + return "Executed test cases : (%d/%d), %d skipped" % [total_count-p_skipped_count, total_count, p_skipped_count] func print_message(message: String, color: Color = _text_color) -> void: @@ -189,3 +200,35 @@ func prints_warning(message: String) -> void: func prints_error(message: String) -> void: _writer.prints_error(message) + + +func total_test_count() -> int: + return _reporter.total_test_count() + + +func total_error_count() -> int: + return _reporter.total_error_count() + + +func total_failure_count() -> int: + return _reporter.total_failure_count() + + +func total_flaky_count() -> int: + return _reporter.total_flaky_count() + + +func total_skipped_count() -> int: + return _reporter.total_skipped_count() + + +func total_orphan_count() -> int: + return _reporter.total_orphan_count() + + +func processed_suite_count() -> int: + return _reporter.processed_suite_count() + + +func elapsed_time() -> int: + return _reporter.elapsed_time() diff --git a/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd.uid b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd.uid new file mode 100644 index 00000000..4bb667a6 --- /dev/null +++ b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd.uid @@ -0,0 +1 @@ +uid://brl4r64uqedki diff --git a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd index 272f76c4..74b961b4 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd +++ b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd @@ -44,9 +44,9 @@ 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:GdUnitTestSuiteReport in test_suite_reports: - var report_link := report.output_path(report_dir).replace(report_dir, "..") + var report_link := GdUnitHtmlReportWriter.create_output_path(report_dir, report.path(), report.name()).replace(report_dir, "..") @warning_ignore("return_value_discarded") - table_records.append(report.create_record(report_link)) + table_records.append(GdUnitHtmlPatterns.create_suite_record(report_link, report)) return template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, "\n".join(table_records)) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid index 942f4489..06a337a3 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid +++ b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid @@ -1 +1 @@ -uid://bn0wlgyhh0y46 +uid://dxy1rn53temob diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd index 03492d5f..b8be904f 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd @@ -78,6 +78,11 @@ ${failure-report} """ +const CHARACTERS_TO_ENCODE := { + '<' : '<', + '>' : '>' +} + const TABLE_BY_PATHS = "${report_table_paths}" const TABLE_BY_TESTSUITES = "${report_table_testsuites}" const TABLE_BY_TESTCASES = "${report_table_tests}" @@ -121,7 +126,7 @@ static func build(template: String, report: GdUnitReportSummary, report_link: St .replace(PATH, get_report_path(report))\ .replace(BREADCRUMP_PATH_LINK, get_path_as_link(report))\ .replace(RESOURCE_PATH, report.get_resource_path())\ - .replace(TESTSUITE_NAME, report.name_html_encoded())\ + .replace(TESTSUITE_NAME, html_encoded(report.name()))\ .replace(TESTSUITE_COUNT, str(report.suite_count()))\ .replace(TESTCASE_COUNT, str(report.test_count()))\ .replace(FAILURE_COUNT, str(report.error_count() + report.failure_count()))\ @@ -161,3 +166,34 @@ 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) + "%" + + +static func html_encoded(value: String) -> String: + for key: String in CHARACTERS_TO_ENCODE.keys(): + @warning_ignore("unsafe_cast") + value = value.replace(key, CHARACTERS_TO_ENCODE[key] as String) + return value + + +static func create_suite_record(report_link: String, report: GdUnitTestSuiteReport) -> String: + return GdUnitHtmlPatterns.build(GdUnitHtmlPatterns.TABLE_RECORD_TESTSUITE, report, report_link) + + +static func create_test_failure_report(_report_dir :String, report: GdUnitTestCaseReport) -> String: + return GdUnitHtmlPatterns.TABLE_RECORD_TESTCASE\ + .replace(GdUnitHtmlPatterns.REPORT_STATE, report.report_state().to_lower())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report.report_state())\ + .replace(GdUnitHtmlPatterns.TESTCASE_NAME, report.name())\ + .replace(GdUnitHtmlPatterns.SKIPPED_COUNT, str(report.skipped_count()))\ + .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(report.orphan_count()))\ + .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(report._duration))\ + .replace(GdUnitHtmlPatterns.FAILURE_REPORT, report.failure_report()) + + +static func create_suite_failure_report(report: GdUnitTestSuiteReport) -> String: + return GdUnitHtmlPatterns.TABLE_REPORT_TESTSUITE\ + .replace(GdUnitHtmlPatterns.REPORT_STATE, report.report_state().to_lower())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report.report_state())\ + .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(report.orphan_count()))\ + .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(report._duration))\ + .replace(GdUnitHtmlPatterns.FAILURE_REPORT, report.failure_report()) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid index 0bcab2de..29282a0d 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid @@ -1 +1 @@ -uid://bum6le67dd51j +uid://fe5o2iqtt76p diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd deleted file mode 100644 index 8ff856c6..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd +++ /dev/null @@ -1,145 +0,0 @@ -class_name GdUnitHtmlReport -extends GdUnitReportSummary - -const REPORT_DIR_PREFIX = "report_" - -var _report_path: String -var _iteration: int -var _max_reports: int - - -func _init(report_path :String, max_reports: int) -> void: - _max_reports = max_reports - if max_reports > 1: - _iteration = GdUnitFileAccess.find_last_path_index(report_path, REPORT_DIR_PREFIX) + 1 - else: - _iteration = 1 - _report_path = "%s/%s%d" % [report_path, REPORT_DIR_PREFIX, _iteration] - @warning_ignore("return_value_discarded") - DirAccess.make_dir_recursive_absolute(_report_path) - - -func add_testsuite_report(p_resource_path: String, p_suite_name: String, p_test_count: int) -> void: - _reports.append(GdUnitTestSuiteReport.new(p_resource_path, p_suite_name, p_test_count)) - - -@warning_ignore("shadowed_variable") -func add_testcase(resource_path :String, suite_name :String, test_name: String) -> void: - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == resource_path: - var test_report := GdUnitTestCaseReport.new(resource_path, suite_name, test_name) - report.add_or_create_test_report(test_report) - - -func add_testsuite_reports( - p_resource_path :String, - p_reports :Array = []) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.set_reports(p_reports) - - -func add_testcase_reports( - p_resource_path: String, - p_test_name: String, - p_reports: Array[GdUnitReport]) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.add_testcase_reports(p_test_name, p_reports) - - -func update_testsuite_counters( - p_resource_path :String, - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_skipped_count: int, - p_flaky_count: int, - p_duration: int) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.update_testsuite_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, p_duration) - update_summary_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, 0) - - -func set_testcase_counters( - p_resource_path: String, - p_test_name: String, - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_is_skipped: bool, - p_is_flaky: bool, - p_duration: int) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.set_testcase_counters(p_test_name, p_error_count, p_failure_count, p_orphan_count, - p_is_skipped, p_is_flaky, p_duration) - - -func update_summary_counters( - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_skipped_count: int, - p_flaky_count: int, - p_duration: int) -> void: - - _error_count += p_error_count - _failure_count += p_failure_count - _orphan_count += p_orphan_count - _skipped_count += p_skipped_count - _flaky_count += p_flaky_count - _duration += p_duration - - -func write() -> void: - var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/index.html") - var to_write := GdUnitHtmlPatterns.build(template, self, "") - to_write = apply_path_reports(_report_path, to_write, _reports) - to_write = apply_testsuite_reports(_report_path, to_write, _reports) - # write report - FileAccess.open(report_file(), FileAccess.WRITE).store_string(to_write) - @warning_ignore("return_value_discarded") - GdUnitFileAccess.copy_directory("res://addons/gdUnit4/src/reporters/html/template/css/", _report_path + "/css") - - -func report_file() -> String: - return "%s/index.html" % _report_path - - -func delete_history() -> int: - return GdUnitFileAccess.delete_path_index_lower_equals_than(_report_path.get_base_dir(), REPORT_DIR_PREFIX, _iteration-_max_reports) - - -func apply_path_reports(report_dir :String, template :String, report_summaries :Array) -> String: - #Dictionary[String, Array[GdUnitReportSummary]] - var path_report_mapping := GdUnitByPathReport.sort_reports_by_path(report_summaries) - var table_records := PackedStringArray() - var paths :Array[String] = [] - paths.append_array(path_report_mapping.keys()) - paths.sort() - for report_path in paths: - var reports: Array[GdUnitReportSummary] = path_report_mapping.get(report_path) - var report := GdUnitByPathReport.new(report_path, reports) - var report_link :String = report.write(report_dir).replace(report_dir, ".") - @warning_ignore("return_value_discarded") - table_records.append(report.create_record(report_link)) - return template.replace(GdUnitHtmlPatterns.TABLE_BY_PATHS, "\n".join(table_records)) - - -func apply_testsuite_reports(report_dir: String, template: String, test_suite_reports: Array[GdUnitReportSummary]) -> String: - var table_records := PackedStringArray() - for report: GdUnitTestSuiteReport in test_suite_reports: - var report_link :String = report.write(report_dir).replace(report_dir, ".") - @warning_ignore("return_value_discarded") - table_records.append(report.create_record(report_link) as String) - return template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, "\n".join(table_records)) - - -func iteration() -> int: - return _iteration diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid deleted file mode 100644 index d8a2eb58..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b5ag6rau5wipc diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd new file mode 100644 index 00000000..a3d2bfff --- /dev/null +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd @@ -0,0 +1,72 @@ +class_name GdUnitHtmlReportWriter +extends GdUnitReportWriter + + +func output_format() -> String: + return "HTML" + + +func write(report_path: String, report: GdUnitReportSummary) -> String: + var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/index.html") + var to_write := GdUnitHtmlPatterns.build(template, report, "") + to_write = _apply_path_reports(report_path, to_write, report.get_reports()) + to_write = _apply_testsuite_reports(report_path, to_write, report.get_reports()) + # write report + DirAccess.make_dir_recursive_absolute(report_path) + var html_report_file := "%s/index.html" % report_path + FileAccess.open(html_report_file, FileAccess.WRITE).store_string(to_write) + @warning_ignore("return_value_discarded") + GdUnitFileAccess.copy_directory("res://addons/gdUnit4/src/reporters/html/template/css/", report_path + "/css") + return html_report_file + + +func _apply_path_reports(report_dir: String, template: String, report_summaries: Array) -> String: + #Dictionary[String, Array[GdUnitReportSummary]] + var path_report_mapping := GdUnitByPathReport.sort_reports_by_path(report_summaries) + var table_records := PackedStringArray() + var paths: Array[String] = [] + paths.append_array(path_report_mapping.keys()) + paths.sort() + for report_at_path in paths: + var reports: Array[GdUnitReportSummary] = path_report_mapping.get(report_at_path) + var report := GdUnitByPathReport.new(report_at_path, reports) + var report_link: String = report.write(report_dir).replace(report_dir, ".") + @warning_ignore("return_value_discarded") + table_records.append(report.create_record(report_link)) + return template.replace(GdUnitHtmlPatterns.TABLE_BY_PATHS, "\n".join(table_records)) + + +func _apply_testsuite_reports(report_dir: String, template: String, test_suite_reports: Array[GdUnitReportSummary]) -> String: + var table_records := PackedStringArray() + for report: GdUnitTestSuiteReport in test_suite_reports: + var report_link: String = _write(report_dir, report).replace(report_dir, ".") + @warning_ignore("return_value_discarded") + table_records.append(GdUnitHtmlPatterns.create_suite_record(report_link, report)) + return template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, "\n".join(table_records)) + + +func _write(report_dir :String, report: GdUnitTestSuiteReport) -> String: + var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/suite_report.html") + template = GdUnitHtmlPatterns.build(template, report, "") + + var report_output_path := create_output_path(report_dir, report.path(), report.name()) + var test_report_table := PackedStringArray() + if not report._failure_reports.is_empty(): + @warning_ignore("return_value_discarded") + test_report_table.append(GdUnitHtmlPatterns.create_suite_failure_report(report)) + for test_report: GdUnitTestCaseReport in report._reports: + @warning_ignore("return_value_discarded") + test_report_table.append(GdUnitHtmlPatterns.create_test_failure_report(report_output_path, test_report)) + + template = template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTCASES, "\n".join(test_report_table)) + + var dir := report_output_path.get_base_dir() + if not DirAccess.dir_exists_absolute(dir): + @warning_ignore("return_value_discarded") + DirAccess.make_dir_recursive_absolute(dir) + FileAccess.open(report_output_path, FileAccess.WRITE).store_string(template) + return report_output_path + + +static func create_output_path(report_dir :String, path: String, name: String) -> String: + return "%s/test_suites/%s.%s.html" % [report_dir, path.replace("/", "."), name] diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd.uid new file mode 100644 index 00000000..f7ae6a98 --- /dev/null +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd.uid @@ -0,0 +1 @@ +uid://c6i7athww5oqi diff --git a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd b/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd deleted file mode 100644 index 976b91f0..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd +++ /dev/null @@ -1,140 +0,0 @@ -class_name GdUnitReportSummary -extends RefCounted - -const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") - -const CHARACTERS_TO_ENCODE := { - '<' : '<', - '>' : '>' -} - -var _resource_path :String -var _name :String -var _test_count := 0 -var _failure_count := 0 -var _error_count := 0 -var _orphan_count := 0 -var _skipped_count := 0 -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) - - -func path() -> String: - return _resource_path.get_base_dir().replace("res://", "") - - -func get_resource_path() -> String: - return _resource_path - - -func suite_count() -> int: - return _reports.size() - - -func suite_executed_count() -> int: - var executed := _reports.size() - for report in _reports: - if report.test_count() == report.skipped_count(): - executed -= 1 - return executed - - -func test_count() -> int: - var count := _test_count - for report in _reports: - count += report.test_count() - return count - - -func test_executed_count() -> int: - return test_count() - skipped_count() - - -func success_count() -> int: - return test_count() - error_count() - failure_count() - flaky_count() - skipped_count() - - -func error_count() -> int: - return _error_count - - -func failure_count() -> int: - return _failure_count - - -func skipped_count() -> int: - return _skipped_count - - -func flaky_count() -> int: - return _flaky_count - - -func orphan_count() -> int: - return _orphan_count - - -func duration() -> int: - return _duration - - -func get_reports() -> Array: - return _reports - - -func add_report(report :GdUnitReportSummary) -> void: - _reports.append(report) - - -func report_state() -> String: - return calculate_state(error_count(), failure_count(), orphan_count(), flaky_count(), skipped_count()) - - -func succes_rate() -> String: - return calculate_succes_rate(test_count(), error_count(), failure_count()) - - -func calculate_state(p_error_count :int, p_failure_count :int, p_orphan_count :int, p_flaky_count: int, p_skipped_count: int) -> String: - if p_error_count > 0: - return "ERROR" - if p_failure_count > 0: - return "FAILED" - if p_flaky_count > 0: - return "FLAKY" - if p_orphan_count > 0: - return "WARNING" - if p_skipped_count > 0: - return "SKIPPED" - return "PASSED" - - -func calculate_succes_rate(p_test_count :int, p_error_count :int, p_failure_count :int) -> String: - if p_failure_count == 0: - return "100%" - var count := p_test_count-p_failure_count-p_error_count - if count < 0: - return "0%" - return "%d" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + "%" - - -func create_summary(_report_dir :String) -> String: - return "" - - -func html_encode(value: String) -> String: - for key: String in CHARACTERS_TO_ENCODE.keys(): - @warning_ignore("unsafe_cast") - value = value.replace(key, CHARACTERS_TO_ENCODE[key] as String) - return value - - -func convert_rtf_to_html(bbcode :String) -> String: - return GdUnitTools.richtext_normalize(bbcode) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid deleted file mode 100644 index 5000024e..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://7atdlx87ycbk diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd deleted file mode 100644 index 5eec799e..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd +++ /dev/null @@ -1,52 +0,0 @@ -class_name GdUnitTestCaseReport -extends GdUnitReportSummary - -var _suite_name :String -var _failure_reports :Array[GdUnitReport] - - -@warning_ignore("shadowed_variable") -func _init(p_resource_path: String, p_suite_name: String, p_test_name: String) -> void: - _resource_path = p_resource_path - _suite_name = p_suite_name - _name = p_test_name - - -func suite_name() -> String: - return _suite_name - - -func failure_report() -> String: - var html_report := "" - for report in get_test_reports(): - html_report += convert_rtf_to_html(str(report)) - return html_report - - -func create_record(_report_dir :String) -> String: - return GdUnitHtmlPatterns.TABLE_RECORD_TESTCASE\ - .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()))\ - .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(_duration))\ - .replace(GdUnitHtmlPatterns.FAILURE_REPORT, failure_report()) - - -func add_testcase_reports(reports: Array[GdUnitReport]) -> void: - _failure_reports.append_array(reports) - - -func set_testcase_counters(p_error_count: int, p_failure_count: int, p_orphan_count: int, - p_is_skipped: bool, p_is_flaky: bool, p_duration: int) -> void: - _error_count = p_error_count - _failure_count = p_failure_count - _orphan_count = p_orphan_count - _skipped_count = p_is_skipped - _flaky_count = p_is_flaky as int - _duration = p_duration - - -func get_test_reports() -> Array[GdUnitReport]: - return _failure_reports diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid deleted file mode 100644 index 11d87bb8..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://by2anuyrjhfwm diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd deleted file mode 100644 index 88677741..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd +++ /dev/null @@ -1,129 +0,0 @@ -class_name GdUnitTestSuiteReport -extends GdUnitReportSummary - -var _time_stamp: int -var _failure_reports: Array[GdUnitReport] = [] - - -func _init(p_resource_path: String, p_name: String, p_test_count: int) -> void: - _resource_path = p_resource_path - _name = p_name - _test_count = p_test_count - _time_stamp = Time.get_unix_time_from_system() as int - - -func create_record(report_link :String) -> String: - return GdUnitHtmlPatterns.build(GdUnitHtmlPatterns.TABLE_RECORD_TESTSUITE, self, report_link) - - -func output_path(report_dir :String) -> String: - return "%s/test_suites/%s.%s.html" % [report_dir, path().replace("/", "."), name()] - - -func failure_report() -> String: - var html_report := "" - for report in _failure_reports: - html_report += convert_rtf_to_html(str(report)) - return html_report - - -func test_suite_failure_report() -> String: - return GdUnitHtmlPatterns.TABLE_REPORT_TESTSUITE\ - .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()) - - -func write(report_dir :String) -> String: - var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/suite_report.html") - template = GdUnitHtmlPatterns.build(template, self, "") - - var report_output_path := output_path(report_dir) - var test_report_table := PackedStringArray() - if not _failure_reports.is_empty(): - @warning_ignore("return_value_discarded") - test_report_table.append(test_suite_failure_report()) - for test_report: GdUnitTestCaseReport in _reports: - @warning_ignore("return_value_discarded") - test_report_table.append(test_report.create_record(report_output_path)) - - template = template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTCASES, "\n".join(test_report_table)) - - var dir := report_output_path.get_base_dir() - if not DirAccess.dir_exists_absolute(dir): - @warning_ignore("return_value_discarded") - DirAccess.make_dir_recursive_absolute(dir) - FileAccess.open(report_output_path, FileAccess.WRITE).store_string(template) - return report_output_path - - -func set_duration(p_duration :int) -> void: - _duration = p_duration - - -func time_stamp() -> int: - return _time_stamp - - -func duration() -> int: - return _duration - - -func set_skipped(skipped :int) -> void: - _skipped_count += skipped - - -func set_orphans(orphans :int) -> void: - _orphan_count = orphans - - -func set_failed(count :int) -> void: - _failure_count += count - - -func set_reports(failure_reports :Array[GdUnitReport]) -> void: - _failure_reports = failure_reports - - -func add_or_create_test_report(test_report: GdUnitTestCaseReport) -> void: - _reports.append(test_report) - - -func update_testsuite_counters( - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_skipped_count: int, - p_flaky_count: int, - p_duration: int) -> void: - _error_count += p_error_count - _failure_count += p_failure_count - _orphan_count += p_orphan_count - _skipped_count += p_skipped_count - _flaky_count += p_flaky_count - _duration += p_duration - - -func set_testcase_counters(test_name: String, p_error_count: int, p_failure_count: int, p_orphan_count: int, - p_is_skipped: bool, p_is_flaky: bool, p_duration: int) -> void: - if _reports.is_empty(): - return - var test_report:GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: - return report.name() == test_name - ).back() - if test_report: - test_report.set_testcase_counters(p_error_count, p_failure_count, p_orphan_count, p_is_skipped, p_is_flaky, p_duration) - - -func add_testcase_reports(test_name: String, reports: Array[GdUnitReport] ) -> void: - if reports.is_empty(): - return - # we lookup to latest matching report because of flaky tests could be retry the tests - # and resultis in multipe report entries with the same name - var test_report:GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: - return report.name() == test_name - ).back() - if test_report: - test_report.add_testcase_reports(reports) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid deleted file mode 100644 index 4be1ea64..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cax8eeslwoudp diff --git a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd similarity index 74% rename from addons/gdUnit4/src/reporters/JUnitXmlReport.gd rename to addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd index c9bdd460..0b9674f3 100644 --- a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd +++ b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd @@ -1,7 +1,7 @@ # This class implements the JUnit XML file format # based checked https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd -class_name JUnitXmlReport -extends RefCounted +class_name JUnitXmlReportWriter +extends GdUnitReportWriter const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") @@ -22,42 +22,40 @@ const ATTR_TYPE := "type" const HEADER := '\n' -var _report_path :String -var _iteration :int +func output_format() -> String: + return "XML" -func _init(path :String, iteration :int) -> void: - _iteration = iteration - _report_path = path - -func write(report :GdUnitReportSummary) -> String: - var result_file: String = "%s/results.xml" % _report_path +func write(report_path: String, report: GdUnitReportSummary) -> String: + var result_file: String = "%s/results.xml" % report_path + DirAccess.make_dir_recursive_absolute(report_path) var file := FileAccess.open(result_file, FileAccess.WRITE) if file == null: push_warning("Can't saving the result to '%s'\n Error: %s" % [result_file, error_string(FileAccess.get_open_error())]) - file.store_string(build_junit_report(report)) + else: + file.store_string(build_junit_report(report_path, report)) return result_file -func build_junit_report(report :GdUnitReportSummary) -> String: +func build_junit_report(report_path: String, report: GdUnitReportSummary) -> String: var iso8601_datetime := Time.get_date_string_from_system() var test_suites := XmlElement.new("testsuites")\ .attribute(ATTR_ID, iso8601_datetime)\ - .attribute(ATTR_NAME, "report_%s" % _iteration)\ + .attribute(ATTR_NAME, report_path.get_file())\ .attribute(ATTR_TESTS, report.test_count())\ .attribute(ATTR_FAILURES, report.failure_count())\ .attribute(ATTR_SKIPPED, report.skipped_count())\ .attribute(ATTR_FLAKY, report.flaky_count())\ - .attribute(ATTR_TIME, JUnitXmlReport.to_time(report.duration()))\ + .attribute(ATTR_TIME, JUnitXmlReportWriter.to_time(report.duration()))\ .add_childs(build_test_suites(report)) var as_string := test_suites.to_xml() test_suites.dispose() return HEADER + as_string -func build_test_suites(summary :GdUnitReportSummary) -> Array: - var test_suites :Array[XmlElement] = [] +func build_test_suites(summary: GdUnitReportSummary) -> Array: + var test_suites: Array[XmlElement] = [] for index in summary.get_reports().size(): var suite_report :GdUnitTestSuiteReport = summary.get_reports()[index] var iso8601_datetime := Time.get_datetime_string_from_unix_time(suite_report.time_stamp()) @@ -72,36 +70,36 @@ func build_test_suites(summary :GdUnitReportSummary) -> Array: .attribute(ATTR_ERRORS, suite_report.error_count())\ .attribute(ATTR_SKIPPED, suite_report.skipped_count())\ .attribute(ATTR_FLAKY, suite_report.flaky_count())\ - .attribute(ATTR_TIME, JUnitXmlReport.to_time(suite_report.duration()))\ + .attribute(ATTR_TIME, JUnitXmlReportWriter.to_time(suite_report.duration()))\ .add_childs(build_test_cases(suite_report))) return test_suites -func build_test_cases(suite_report :GdUnitTestSuiteReport) -> Array: - var test_cases :Array[XmlElement] = [] +func build_test_cases(suite_report: GdUnitTestSuiteReport) -> Array: + var test_cases: Array[XmlElement] = [] for index in suite_report.get_reports().size(): var report :GdUnitTestCaseReport = suite_report.get_reports()[index] test_cases.append( XmlElement.new("testcase")\ - .attribute(ATTR_NAME, JUnitXmlReport.encode_xml(report.name()))\ + .attribute(ATTR_NAME, JUnitXmlReportWriter.encode_xml(report.name()))\ .attribute(ATTR_CLASSNAME, report.suite_name())\ - .attribute(ATTR_TIME, JUnitXmlReport.to_time(report.duration()))\ + .attribute(ATTR_TIME, JUnitXmlReportWriter.to_time(report.duration()))\ .add_childs(build_reports(report))) return test_cases func build_reports(test_report: GdUnitTestCaseReport) -> Array: - var failure_reports :Array[XmlElement] = [] + var failure_reports: Array[XmlElement] = [] for report: GdUnitReport in test_report.get_test_reports(): if report.is_failure(): failure_reports.append(XmlElement.new("failure")\ .attribute(ATTR_MESSAGE, "FAILED: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ - .attribute(ATTR_TYPE, JUnitXmlReport.to_type(report.type()))\ + .attribute(ATTR_TYPE, JUnitXmlReportWriter.to_type(report.type()))\ .text(convert_rtf_to_text(report.message()))) elif report.is_error(): failure_reports.append(XmlElement.new("error")\ .attribute(ATTR_MESSAGE, "ERROR: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ - .attribute(ATTR_TYPE, JUnitXmlReport.to_type(report.type()))\ + .attribute(ATTR_TYPE, JUnitXmlReportWriter.to_type(report.type()))\ .text(convert_rtf_to_text(report.message()))) elif report.is_skipped(): failure_reports.append(XmlElement.new("skipped")\ @@ -110,11 +108,11 @@ func build_reports(test_report: GdUnitTestCaseReport) -> Array: return failure_reports -func convert_rtf_to_text(bbcode :String) -> String: +func convert_rtf_to_text(bbcode: String) -> String: return GdUnitTools.richtext_normalize(bbcode) -static func to_type(type :int) -> String: +static func to_type(type: int) -> String: match type: GdUnitReport.SUCCESS: return "SUCCESS" @@ -133,11 +131,11 @@ static func to_type(type :int) -> String: return "UNKNOWN" -static func to_time(duration :int) -> String: +static func to_time(duration: int) -> String: return "%4.03f" % (duration / 1000.0) -static func encode_xml(value :String) -> String: +static func encode_xml(value: String) -> String: return value.xml_escape(true) diff --git a/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd.uid b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd.uid new file mode 100644 index 00000000..ac785437 --- /dev/null +++ b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd.uid @@ -0,0 +1 @@ +uid://dkiwc784nneu2 diff --git a/addons/gdUnit4/src/reporters/XmlElement.gd b/addons/gdUnit4/src/reporters/xml/XmlElement.gd similarity index 100% rename from addons/gdUnit4/src/reporters/XmlElement.gd rename to addons/gdUnit4/src/reporters/xml/XmlElement.gd diff --git a/addons/gdUnit4/src/reporters/xml/XmlElement.gd.uid b/addons/gdUnit4/src/reporters/xml/XmlElement.gd.uid new file mode 100644 index 00000000..3957f506 --- /dev/null +++ b/addons/gdUnit4/src/reporters/xml/XmlElement.gd.uid @@ -0,0 +1 @@ +uid://rmjxtxqq70cp diff --git a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd index bee38326..5d8b6318 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd +++ b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd @@ -41,7 +41,7 @@ static func build(to_spy: Variant, debug_write := false) -> Variant: var spy_instance: Object = spy.new() @warning_ignore("unsafe_method_access") # we do not call the original implementation for _ready and all input function, this is actualy done by the engine - spy_instance.__init(to_spy, ["_input", "_gui_input", "_input_event", "_unhandled_input"]) + spy_instance.__init(["_input", "_gui_input", "_input_event", "_unhandled_input"]) @warning_ignore("unsafe_cast") copy_properties(to_spy as Object, spy_instance) @warning_ignore("return_value_discarded") @@ -58,7 +58,7 @@ static func get_class_info(clazz :Variant) -> Dictionary: } -static func spy_on_script(instance :Variant, function_excludes :PackedStringArray, debug_write :bool) -> GDScript: +static func spy_on_script(instance: Variant, function_excludes: PackedStringArray, debug_write: bool) -> GDScript: if GdArrayTools.is_array_type(instance): if GdUnitSettings.is_verbose_assert_errors(): push_error("Can't build spy checked type '%s'! Spy checked Container Built-In Type not supported!" % type_string(typeof(instance))) @@ -70,10 +70,20 @@ static func spy_on_script(instance :Variant, function_excludes :PackedStringArra if GdUnitSettings.is_verbose_assert_errors(): push_error("Can't build spy for class type '%s'! Using an instance instead e.g. 'spy()'" % [clazz_name]) return null + + @warning_ignore("unsafe_method_access") + var spy_template := SPY_TEMPLATE.source_code.format({ + "instance_id" : abs(instance.get_instance_id()), + "gdunit_source_class": clazz_name if clazz_path.is_empty() else clazz_path[0] + }) @warning_ignore("unsafe_cast") - var lines := load_template(SPY_TEMPLATE.source_code, class_info, instance as Object) + var lines := load_template(spy_template, class_info) @warning_ignore("unsafe_cast") lines += double_functions(instance as Object, clazz_name, clazz_path, GdUnitSpyFunctionDoubler.new(), function_excludes) + # We disable warning/errors for inferred_declaration + if Engine.get_version_info().hex >= 0x40400: + lines.insert(0, '@warning_ignore_start("inferred_declaration")') + lines.append('@warning_ignore_restore("inferred_declaration")') var spy := GDScript.new() spy.source_code = "\n".join(lines) @@ -120,7 +130,7 @@ static func spy_on_scene(scene :Node, debug_write :bool) -> Object: scene.set(property_name, original_properties[property_name]) @warning_ignore("unsafe_method_access") - scene.__init(scene, []) + scene.__init() return register_auto_free(scene) diff --git a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid index cc502b71..680829a3 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid +++ b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid @@ -1 +1 @@ -uid://ckl63mraercmh +uid://bt0uxa2ciej7d diff --git a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd deleted file mode 100644 index c71cea7e..00000000 --- a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd +++ /dev/null @@ -1,114 +0,0 @@ -class_name GdUnitSpyFunctionDoubler -extends GdFunctionDoubler - - -const TEMPLATE_RETURN_VARIANT = """ - var args__: Array = ["$(func_name)", $(arguments)] - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)"): - @warning_ignore("unsafe_call_argument") - return $(await)super($(arguments)) - return ${default_return_value} - -""" - - -const TEMPLATE_RETURN_VOID = """ - var args__: Array = ["$(func_name)", $(arguments)] - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)"): - @warning_ignore("unsafe_call_argument") - $(await)super($(arguments)) - -""" - - -const TEMPLATE_RETURN_VOID_VARARG = """ - var varargs__: Array = __get_verifier().filter_vargs([$(varargs)]) - var args__: Array = ["$(func_name)", $(arguments)] + varargs__ - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return - else: - __verifier.save_function_interaction(args__) - - $(await)__call_func("$(func_name)", [$(arguments)] + varargs__) - -""" - - -const TEMPLATE_RETURN_VARIANT_VARARG = """ - var varargs__: Array = __get_verifier().filter_vargs([$(varargs)]) - var args__: Array = ["$(func_name)", $(arguments)] + varargs__ - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - return $(await)__call_func("$(func_name)", [$(arguments)] + varargs__) - -""" - - -const TEMPLATE_CALLABLE_CALL = """ - var used_arguments__ := __get_verifier().filter_vargs([$(arguments)]) - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(["call", used_arguments__]) - return ${default_return_value}$(return_as) - else: - var args__ := used_arguments__.duplicate() - args__.append_array(super.get_bound_arguments()) - __verifier.save_function_interaction(["call", args__]) - - if __do_call_real_func("call"): - return _cb.callv(used_arguments__) - - return ${default_return_value} - -""" - - -func _init(push_errors: bool = false) -> void: - super._init(push_errors) - - -func get_template(fd: GdFunctionDescriptor, is_callable: bool) -> String: - if is_callable and fd.name() == "call": - return TEMPLATE_CALLABLE_CALL - if fd.is_vararg(): - return TEMPLATE_RETURN_VOID_VARARG if fd.return_type() == TYPE_NIL else TEMPLATE_RETURN_VARIANT_VARARG - var return_type :Variant = fd.return_type() - if return_type is StringName: - return TEMPLATE_RETURN_VARIANT - return TEMPLATE_RETURN_VOID if (return_type == TYPE_NIL or return_type == GdObjects.TYPE_VOID) else TEMPLATE_RETURN_VARIANT diff --git a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid deleted file mode 100644 index 1af54be8..00000000 --- a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4dj47ywfod75 diff --git a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd index e3d2fe86..e0bcaf2b 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd +++ b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd @@ -1,64 +1,46 @@ class_name DoubledSpyClassSourceClassName -const __INSTANCE_ID = "${instance_id}" -const __SOURCE_CLASS = "${source_class}" +const __INSTANCE_ID := "gdunit_doubler_instance_id_{instance_id}" -class SpyState: - var instance_delegator :Object - var excluded_methods :PackedStringArray = [] +class GdUnitSpyDoublerState: + const __SOURCE_CLASS := "{gdunit_source_class}" + var excluded_methods := PackedStringArray() - func call_func(func_name: String, arguments: Array) -> Variant: - return instance_delegator.callv(func_name, arguments) + func _init(excluded_methods__ := PackedStringArray()) -> void: + excluded_methods = excluded_methods__ +var __spy_state := GdUnitSpyDoublerState.new() @warning_ignore("unused_private_class_variable") var __verifier_instance := GdUnitObjectInteractionsVerifier.new() -var __spying_state := SpyState.new() -func __init(__delegator: Object, __exluded_methods := PackedStringArray()) -> void: - # store self need to access static functions - Engine.set_meta(__INSTANCE_ID, self) - __spying_state.instance_delegator = __delegator - __spying_state.excluded_methods = __exluded_methods - - -func __release_double() -> void: - # we need to release the self reference manually to prevent orphan nodes - Engine.remove_meta(__INSTANCE_ID) - __spying_state.instance_delegator = null - - -func _notification(what: int) -> void: - if what == NOTIFICATION_PREDELETE: - if Engine.has_meta(__INSTANCE_ID): - Engine.remove_meta(__INSTANCE_ID) +func __init(__excluded_methods := PackedStringArray()) -> void: + __init_doubler() + __spy_state.excluded_methods = __excluded_methods -static func __get_verifier() -> GdUnitObjectInteractionsVerifier: - var __instance := __get_instance() - @warning_ignore("unsafe_property_access") - return null if __instance == null else __instance.__verifier_instance +static func __doubler_state() -> GdUnitSpyDoublerState: + if Engine.has_meta(__INSTANCE_ID): + return Engine.get_meta(__INSTANCE_ID).__spy_state + return null -static func __spy_state() -> SpyState: - @warning_ignore("unsafe_property_access") - return __get_instance().__spying_state +func __init_doubler() -> void: + Engine.set_meta(__INSTANCE_ID, self) -static func __get_instance() -> Object: - return null if not Engine.has_meta(__INSTANCE_ID) else Engine.get_meta(__INSTANCE_ID) +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE and Engine.has_meta(__INSTANCE_ID): + Engine.remove_meta(__INSTANCE_ID) -func __instance_id() -> String: - return __INSTANCE_ID +static func __get_verifier() -> GdUnitObjectInteractionsVerifier: + return Engine.get_meta(__INSTANCE_ID).__verifier_instance static func __do_call_real_func(__func_name: String) -> bool: - return not __spy_state().excluded_methods.has(__func_name) - - -static func __call_func(__func_name: String, __arguments: Array) -> Variant: - return __spy_state().call_func(__func_name, __arguments) + @warning_ignore("unsafe_method_access") + return not __doubler_state().excluded_methods.has(__func_name) diff --git a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid index 587e1f9f..8c80e66a 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid +++ b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid @@ -1 +1 @@ -uid://b8havs2yug2n2 +uid://bfqcsq5jn6lp5 diff --git a/addons/gdUnit4/src/ui/GdUnitConsole.gd b/addons/gdUnit4/src/ui/GdUnitConsole.gd index 36a94d43..eb1e6254 100644 --- a/addons/gdUnit4/src/ui/GdUnitConsole.gd +++ b/addons/gdUnit4/src/ui/GdUnitConsole.gd @@ -71,7 +71,11 @@ func setup_update_notification(control: Button) -> void: func _on_gdunit_event(event: GdUnitEvent) -> void: - _test_reporter.on_gdunit_event(event) + match event.type(): + GdUnitEvent.SESSION_START: + _test_reporter.test_session = GdUnitTestSession.new(GdUnitTestDiscoverGuard.instance().get_discovered_tests(), "") + GdUnitEvent.SESSION_CLOSE: + _test_reporter.test_session = null func _on_gdunit_client_connected(client_id: int) -> void: diff --git a/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid b/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid index e0dc580c..7f5e9bda 100644 --- a/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid @@ -1 +1 @@ -uid://cs1ctx62mqqjx +uid://n68dicefeqid diff --git a/addons/gdUnit4/src/ui/GdUnitConsole.tscn b/addons/gdUnit4/src/ui/GdUnitConsole.tscn index 16667144..c3c7e29f 100644 --- a/addons/gdUnit4/src/ui/GdUnitConsole.tscn +++ b/addons/gdUnit4/src/ui/GdUnitConsole.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://dm0wvfyeew7vd"] -[ext_resource type="Script" uid="uid://cs1ctx62mqqjx" path="res://addons/gdUnit4/src/ui/GdUnitConsole.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/GdUnitConsole.gd" id="1"] [node name="Control" type="Control"] use_parent_material = true diff --git a/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid b/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid index 566bf48c..9f63ada8 100644 --- a/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid @@ -1 +1 @@ -uid://bom64ynmymmxn +uid://lemhoxkt3y34 diff --git a/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid b/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid index 0bf865d3..ba0422b3 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid @@ -1 +1 @@ -uid://cr1f2bvyqxnjc +uid://bjm5ad14h752s diff --git a/addons/gdUnit4/src/ui/GdUnitInspector.tscn b/addons/gdUnit4/src/ui/GdUnitInspector.tscn index 29733bc6..2dcb9505 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspector.tscn +++ b/addons/gdUnit4/src/ui/GdUnitInspector.tscn @@ -1,12 +1,12 @@ [gd_scene load_steps=8 format=3 uid="uid://mpo5o6d4uybu"] -[ext_resource type="PackedScene" uid="uid://dx7xy4dgi3wwb" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn" id="1"] -[ext_resource type="PackedScene" uid="uid://dva3tonxsxrlk" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn" id="2"] -[ext_resource type="PackedScene" uid="uid://c22l4odk7qesc" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn" id="3"] -[ext_resource type="PackedScene" uid="uid://djp8ait0bxpsc" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn" id="4"] -[ext_resource type="Script" uid="uid://cr1f2bvyqxnjc" path="res://addons/gdUnit4/src/ui/GdUnitInspector.gd" id="5"] -[ext_resource type="PackedScene" uid="uid://bqfpidewtpeg0" path="res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn" id="7"] -[ext_resource type="PackedScene" uid="uid://cn5mp3tmi2gb1" path="res://addons/gdUnit4/src/network/GdUnitServer.tscn" id="7_721no"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn" id="1"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn" id="2"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn" id="3"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn" id="4"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/GdUnitInspector.gd" id="5"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn" id="7"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/network/GdUnitServer.tscn" id="7_721no"] [node name="GdUnit" type="Panel"] use_parent_material = true @@ -26,12 +26,14 @@ layout_mode = 0 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_vertical = 11 +theme_override_constants/separation = 0 [node name="Header" type="VBoxContainer" parent="VBoxContainer"] use_parent_material = true clip_contents = true layout_mode = 2 size_flags_horizontal = 9 +size_flags_vertical = 0 [node name="ToolBar" parent="VBoxContainer/Header" instance=ExtResource("1")] layout_mode = 2 @@ -57,11 +59,13 @@ layout_mode = 2 [node name="event_server" parent="." instance=ExtResource("7_721no")] [connection signal="request_discover_tests" from="VBoxContainer/Header/StatusBar" to="." method="_on_status_bar_request_discover_tests"] -[connection signal="select_error_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [6]] -[connection signal="select_error_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [6]] -[connection signal="select_failure_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [5]] -[connection signal="select_failure_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [5]] -[connection signal="select_flaky_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [4]] -[connection signal="select_flaky_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [4]] +[connection signal="select_error_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [7]] +[connection signal="select_error_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [7]] +[connection signal="select_failure_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [6]] +[connection signal="select_failure_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [6]] +[connection signal="select_flaky_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [5]] +[connection signal="select_flaky_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [5]] +[connection signal="select_skipped_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [2]] +[connection signal="select_skipped_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [2]] [connection signal="tree_view_mode_changed" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_status_bar_tree_view_mode_changed"] [connection signal="jump_to_orphan_nodes" from="VBoxContainer/Monitor" to="VBoxContainer/MainPanel" method="select_first_orphan"] diff --git a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd index 01315d83..8dd0265d 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd +++ b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd @@ -21,11 +21,11 @@ enum SORT_MODE { enum STATE { INITIAL, RUNNING, + SKIPPED, SUCCESS, WARNING, FLAKY, FAILED, ERROR, ABORDED, - SKIPPED } diff --git a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid index 0e258b0b..120f36f7 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid @@ -1 +1 @@ -uid://blq343qe55r1g +uid://cdbpdnafpsydh diff --git a/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid b/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid index a6e610d6..93aeb0f5 100644 --- a/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid @@ -1 +1 @@ -uid://b6q6qgxe63q2p +uid://ciy84yv3s01c6 diff --git a/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid b/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid index c83b1295..241ae9f0 100644 --- a/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid +++ b/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid @@ -1 +1 @@ -uid://83v134w2n7r1 +uid://day68pf4tlcuq diff --git a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid index 61c68d9d..4c5f7348 100644 --- a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid +++ b/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid @@ -1 +1 @@ -uid://tnxos2h6ikg +uid://b5814vvbhytx0 diff --git a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid index bfc0ee40..d688542e 100644 --- a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid +++ b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid @@ -1 +1 @@ -uid://c2o2b4yxslhpp +uid://demquconmea08 diff --git a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid index 1605a176..dd265195 100644 --- a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid +++ b/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid @@ -1 +1 @@ -uid://dn7xwregxtmxu +uid://cqu60wu0jk4pw diff --git a/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid index d34c2457..73f3229c 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid @@ -1 +1 @@ -uid://c8m6jyq2fq1wx +uid://c62lwwyqrget3 diff --git a/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn b/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn index 26d64cc8..0262b8cd 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://djp8ait0bxpsc"] -[ext_resource type="Script" uid="uid://c8m6jyq2fq1wx" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd" id="3"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd" id="3"] [sub_resource type="Image" id="Image_sx31i"] data = { diff --git a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd index 0404bd2c..d368ed3f 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd +++ b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd @@ -5,6 +5,7 @@ extends ProgressBar @onready var status: Label = $Label @onready var style: StyleBoxFlat = get("theme_override_styles/fill") +var _state: GdUnitInspectorTreeConstants.STATE func _ready() -> void: style.bg_color = Color.DARK_GREEN @@ -12,6 +13,7 @@ func _ready() -> void: max_value = 0 update_text() + func update_text() -> void: status.text = "%d:%d" % [value, max_value] @@ -19,14 +21,21 @@ func update_text() -> void: func _on_test_counter_changed(index: int, total: int, state: GdUnitInspectorTreeConstants.STATE) -> void: value = index max_value = total + update_text() + # inital state if index == 0: style.bg_color = Color.DARK_GREEN + + # do only update the state is higher prio than current state + if state <= _state: + return + _state = state + if is_flaky(state): style.bg_color = Color.WEB_GREEN if is_failed(state): style.bg_color = Color.DARK_RED - update_text() func is_failed(state: GdUnitInspectorTreeConstants.STATE) -> bool: diff --git a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid index 4aa83981..4e66a530 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid @@ -1 +1 @@ -uid://ckofa77q303f2 +uid://swltuya022dq diff --git a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn index 836544b9..1824230a 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://dva3tonxsxrlk"] -[ext_resource type="Script" uid="uid://ckofa77q303f2" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd" id="1"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ayfir"] bg_color = Color(0, 0.392157, 0, 1) diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd index f2623aeb..dfed5204 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd +++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd @@ -7,6 +7,8 @@ signal select_error_next() signal select_error_prevous() signal select_flaky_next() signal select_flaky_prevous() +signal select_skipped_next() +signal select_skipped_prevous() signal request_discover_tests() @warning_ignore("unused_signal") @@ -15,8 +17,9 @@ signal tree_view_mode_changed(flat :bool) @onready var _errors: Label = %error_value @onready var _failures: Label = %failure_value @onready var _flaky_value: Label = %flaky_value -@onready var _button_failure_up: Button = %btn_failure_up -@onready var _button_failure_down: Button = %btn_failure_down +@onready var _skipped_value: Label = %skipped_value +#@onready var _button_failure_up: Button = %btn_failure_up +#@onready var _button_failure_down: Button = %btn_failure_down @onready var _button_sync: Button = %btn_tree_sync @onready var _button_view_mode: MenuButton = %btn_tree_mode @onready var _button_sort_mode: MenuButton = %btn_tree_sort @@ -24,10 +27,12 @@ signal tree_view_mode_changed(flat :bool) @onready var _icon_errors: TextureRect = %icon_errors @onready var _icon_failures: TextureRect = %icon_failures @onready var _icon_flaky: TextureRect = %icon_flaky +@onready var _icon_skipped: TextureRect = %icon_skipped var total_failed := 0 var total_errors := 0 var total_flaky := 0 +var total_skipped := 0 var icon_mappings := { @@ -46,12 +51,15 @@ var icon_mappings := { func _ready() -> void: _failures.text = "0" _errors.text = "0" + _flaky_value.text = "0" + _skipped_value.text = "0" _icon_failures.texture = GdUnitUiTools.get_icon("StatusError", Color.SKY_BLUE) _icon_errors.texture = GdUnitUiTools.get_icon("StatusError", Color.DARK_RED) _icon_flaky.texture = GdUnitUiTools.get_icon("CheckBox", Color.GREEN_YELLOW) + _icon_skipped.texture = GdUnitUiTools.get_icon("CheckBox", Color.WEB_GRAY) - _button_failure_up.icon = GdUnitUiTools.get_icon("ArrowUp") - _button_failure_down.icon = GdUnitUiTools.get_icon("ArrowDown") + #_button_failure_up.icon = GdUnitUiTools.get_icon("ArrowUp") + #_button_failure_down.icon = GdUnitUiTools.get_icon("ArrowDown") _button_sync.icon = GdUnitUiTools.get_icon("Loop") _set_sort_mode_menu_options() _set_view_mode_menu_options() @@ -105,13 +113,15 @@ func normalise(value: String) -> String: return " ".join(parts) -func status_changed(errors: int, failed: int, flaky: int) -> void: +func status_changed(errors: int, failed: int, flaky: int, skipped: int) -> void: total_failed += failed total_errors += errors total_flaky += flaky + total_skipped += skipped _failures.text = str(total_failed) _errors.text = str(total_errors) _flaky_value.text = str(total_flaky) + _skipped_value.text = str(total_skipped) func disable_buttons(value :bool) -> void: @@ -129,16 +139,17 @@ func _on_gdunit_event(event: GdUnitEvent) -> void: disable_buttons(false) GdUnitEvent.INIT: - total_failed = 0 total_errors = 0 + total_failed = 0 total_flaky = 0 - status_changed(0, 0, 0) + total_skipped = 0 + status_changed(total_errors, total_failed, total_flaky, total_skipped) GdUnitEvent.TESTCASE_AFTER: - status_changed(event.error_count(), event.failed_count(), event.is_flaky()) + status_changed(event.error_count(), event.failed_count(), event.is_flaky(), event.is_skipped()) - #GdUnitEvent.TESTSUITE_AFTER: - # status_changed(event.error_count(), event.failed_count(), event.is_flaky()) + GdUnitEvent.TESTSUITE_AFTER: + status_changed(event.error_count(), event.failed_count(), event.is_flaky(), 0) func _on_btn_error_up_pressed() -> void: @@ -165,6 +176,14 @@ func _on_btn_flaky_down_pressed() -> void: select_flaky_next.emit() +func _on_btn_skipped_up_pressed() -> void: + select_skipped_prevous.emit() + + +func _on_btn_skipped_down_pressed() -> void: + select_skipped_next.emit() + + func _on_tree_sync_pressed() -> void: request_discover_tests.emit() diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid index 3d5bc48c..c30a1a9f 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid @@ -1 +1 @@ -uid://kpqps1ctk13s +uid://bn15x27e4ofrf diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn index 16066e0e..baf6a9fb 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn @@ -1,116 +1,116 @@ -[gd_scene load_steps=32 format=3 uid="uid://c22l4odk7qesc"] +[gd_scene load_steps=30 format=3 uid="uid://c22l4odk7qesc"] -[ext_resource type="Script" uid="uid://kpqps1ctk13s" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd" id="3"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd" id="3"] [sub_resource type="Image" id="Image_mb3ih"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 160, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 213, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 75, 224, 224, 224, 188, 224, 224, 224, 238, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 224, 224, 224, 96, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 226, 226, 226, 95, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 77, 224, 224, 224, 255, 224, 224, 224, 253, 225, 225, 225, 117, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 212, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 129, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 189, 224, 224, 224, 255, 224, 224, 224, 113, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 159, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 73, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 242, 224, 224, 224, 255, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 25, 224, 224, 224, 255, 224, 224, 224, 238, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 243, 224, 224, 224, 254, 233, 233, 233, 23, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 29, 224, 224, 224, 255, 224, 224, 224, 236, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 189, 224, 224, 224, 255, 225, 225, 225, 68, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 224, 224, 224, 160, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 121, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 72, 224, 224, 224, 121, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 43, 224, 224, 224, 213, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 225, 225, 225, 124, 224, 224, 224, 254, 224, 224, 224, 255, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 96, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 125, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 95, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 224, 224, 224, 185, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 213, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 225, 225, 225, 159, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 160, 230, 230, 230, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 213, 225, 225, 225, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 75, 224, 224, 224, 188, 224, 224, 224, 238, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 224, 224, 224, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 226, 226, 226, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 77, 224, 224, 224, 255, 224, 224, 224, 253, 225, 225, 225, 117, 224, 224, 224, 32, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 212, 225, 225, 225, 42, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 129, 226, 226, 226, 70, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 189, 224, 224, 224, 255, 224, 224, 224, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 159, 230, 230, 230, 10, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 73, 224, 224, 224, 255, 224, 224, 224, 185, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 242, 224, 224, 224, 255, 224, 224, 224, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 25, 224, 224, 224, 255, 224, 224, 224, 238, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 243, 224, 224, 224, 254, 233, 233, 233, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 229, 229, 29, 224, 224, 224, 255, 224, 224, 224, 236, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 189, 224, 224, 224, 255, 225, 225, 225, 68, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 10, 224, 224, 224, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 121, 224, 224, 224, 255, 224, 224, 224, 181, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 72, 224, 224, 224, 121, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 43, 224, 224, 224, 213, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 227, 227, 227, 36, 225, 225, 225, 124, 224, 224, 224, 254, 224, 224, 224, 255, 226, 226, 226, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 96, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 95, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 224, 224, 224, 185, 226, 226, 226, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 42, 224, 224, 224, 213, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 10, 225, 225, 225, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_jvn24"] +[sub_resource type="ImageTexture" id="ImageTexture_wo03e"] image = SubResource("Image_mb3ih") -[sub_resource type="Image" id="Image_wo03e"] +[sub_resource type="Image" id="Image_ixycx"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_k82x4"] -image = SubResource("Image_wo03e") +[sub_resource type="ImageTexture" id="ImageTexture_c80wp"] +image = SubResource("Image_ixycx") -[sub_resource type="Image" id="Image_ixycx"] +[sub_resource type="Image" id="Image_eis20"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_bs7qq"] -image = SubResource("Image_ixycx") +[sub_resource type="ImageTexture" id="ImageTexture_t2qd7"] +image = SubResource("Image_eis20") -[sub_resource type="Image" id="Image_c80wp"] +[sub_resource type="Image" id="Image_jh28t"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_0ck6a"] -image = SubResource("Image_c80wp") +[sub_resource type="ImageTexture" id="ImageTexture_1mh1t"] +image = SubResource("Image_jh28t") -[sub_resource type="Image" id="Image_eis20"] +[sub_resource type="Image" id="Image_lpjla"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 3, 224, 224, 224, 105, 224, 224, 224, 192, 224, 224, 224, 244, 224, 224, 224, 238, 224, 224, 224, 197, 224, 224, 224, 105, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 225, 225, 225, 207, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 198, 226, 226, 226, 26, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 6, 224, 224, 224, 205, 224, 224, 224, 255, 224, 224, 224, 218, 225, 225, 225, 83, 237, 237, 237, 14, 237, 237, 237, 14, 224, 224, 224, 82, 224, 224, 224, 220, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 102, 224, 224, 224, 255, 224, 224, 224, 218, 227, 227, 227, 18, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 16, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 198, 224, 224, 224, 255, 225, 225, 225, 84, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 86, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 4, 224, 224, 224, 238, 224, 224, 224, 255, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 229, 229, 229, 19, 224, 224, 224, 255, 224, 224, 224, 233, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 160, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 159, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 237, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 90, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 245, 224, 224, 224, 245, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 232, 232, 232, 22, 224, 224, 224, 224, 224, 224, 224, 255, 224, 224, 224, 98, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 96, 226, 226, 226, 95, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 20, 224, 224, 224, 88, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 200, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 236, 224, 224, 224, 195, 224, 224, 224, 96, 255, 255, 255, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 3, 224, 224, 224, 105, 224, 224, 224, 192, 224, 224, 224, 244, 224, 224, 224, 238, 224, 224, 224, 197, 224, 224, 224, 105, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, 233, 23, 225, 225, 225, 207, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 198, 226, 226, 226, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 6, 224, 224, 224, 205, 224, 224, 224, 255, 224, 224, 224, 218, 225, 225, 225, 83, 237, 237, 237, 14, 237, 237, 237, 14, 224, 224, 224, 82, 224, 224, 224, 220, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 102, 224, 224, 224, 255, 224, 224, 224, 218, 227, 227, 227, 18, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 224, 224, 224, 16, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 198, 224, 224, 224, 255, 225, 225, 225, 84, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 226, 226, 226, 86, 224, 224, 224, 255, 224, 224, 224, 194, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 1, 255, 255, 255, 4, 224, 224, 224, 238, 224, 224, 224, 255, 227, 227, 227, 18, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 229, 229, 229, 19, 224, 224, 224, 255, 224, 224, 224, 233, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 160, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 159, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 237, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 10, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 230, 230, 230, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 90, 224, 224, 224, 255, 224, 224, 224, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 42, 224, 224, 224, 245, 224, 224, 224, 245, 225, 225, 225, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 232, 232, 22, 224, 224, 224, 224, 224, 224, 224, 255, 224, 224, 224, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 96, 226, 226, 226, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 20, 224, 224, 224, 88, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 200, 227, 227, 227, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 236, 224, 224, 224, 195, 224, 224, 224, 96, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_t7ac1"] -image = SubResource("Image_eis20") +[sub_resource type="ImageTexture" id="ImageTexture_bq8kn"] +image = SubResource("Image_lpjla") -[sub_resource type="Image" id="Image_t2qd7"] +[sub_resource type="Image" id="Image_bwbka"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_03vfp"] -image = SubResource("Image_t2qd7") +[sub_resource type="ImageTexture" id="ImageTexture_8lbfl"] +image = SubResource("Image_bwbka") -[sub_resource type="Image" id="Image_jh28t"] +[sub_resource type="Image" id="Image_ki3oo"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_fv3i4"] -image = SubResource("Image_jh28t") +[sub_resource type="ImageTexture" id="ImageTexture_ivm1h"] +image = SubResource("Image_ki3oo") -[sub_resource type="Image" id="Image_1mh1t"] +[sub_resource type="Image" id="Image_uqb0l"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 249, 249, 255, 230, 246, 246, 252, 230, 249, 249, 255, 230, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 237, 246, 246, 252, 255, 246, 246, 252, 248, 255, 255, 255, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 236, 246, 246, 252, 254, 246, 246, 252, 247, 255, 255, 255, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 253, 231, 246, 246, 253, 232, 246, 246, 252, 230, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 243, 246, 246, 252, 255, 246, 246, 252, 242, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 242, 246, 246, 252, 253, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 249, 255, 230, 246, 246, 252, 230, 249, 249, 255, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 237, 246, 246, 252, 255, 246, 246, 252, 248, 0, 0, 0, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 236, 246, 246, 252, 254, 246, 246, 252, 247, 0, 0, 0, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 253, 231, 246, 246, 253, 232, 246, 246, 252, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 243, 246, 246, 252, 255, 246, 246, 252, 242, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 242, 246, 246, 252, 253, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_ab51p"] -image = SubResource("Image_1mh1t") +[sub_resource type="ImageTexture" id="ImageTexture_j00vj"] +image = SubResource("Image_uqb0l") -[sub_resource type="Image" id="Image_lpjla"] +[sub_resource type="Image" id="Image_0oden"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 122, 111, 23, 255, 121, 107, 126, 255, 120, 108, 206, 255, 120, 107, 240, 255, 120, 107, 240, 255, 120, 108, 206, 255, 121, 107, 124, 255, 128, 116, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 108, 80, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 121, 107, 239, 255, 123, 109, 77, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 108, 78, 255, 120, 107, 254, 255, 120, 107, 255, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 254, 255, 122, 109, 75, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 116, 22, 255, 121, 107, 239, 255, 120, 107, 255, 255, 122, 107, 107, 255, 121, 109, 42, 255, 120, 107, 233, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 231, 255, 121, 108, 40, 255, 121, 107, 112, 255, 120, 107, 255, 255, 120, 107, 238, 255, 128, 115, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 124, 255, 120, 107, 255, 255, 120, 107, 240, 255, 121, 109, 42, 255, 255, 255, 0, 255, 121, 109, 42, 255, 120, 107, 233, 255, 120, 107, 232, 255, 124, 112, 41, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 119, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 207, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 121, 109, 42, 255, 255, 255, 0, 255, 121, 109, 42, 255, 121, 109, 42, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 202, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 121, 109, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 108, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 232, 255, 121, 109, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 122, 110, 44, 255, 120, 107, 234, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 108, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 207, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 231, 255, 124, 112, 41, 255, 255, 255, 0, 255, 125, 108, 45, 255, 122, 110, 44, 255, 255, 255, 0, 255, 125, 107, 43, 255, 120, 107, 233, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 200, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 108, 123, 255, 120, 107, 255, 255, 120, 107, 240, 255, 121, 108, 40, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 234, 255, 125, 107, 43, 255, 255, 255, 0, 255, 125, 107, 43, 255, 120, 107, 242, 255, 120, 107, 255, 255, 121, 108, 116, 255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 116, 22, 255, 120, 107, 238, 255, 120, 107, 255, 255, 121, 107, 112, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 125, 107, 43, 255, 120, 107, 117, 255, 120, 107, 255, 255, 120, 107, 235, 255, 128, 113, 18, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 76, 255, 120, 107, 254, 255, 120, 107, 255, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 253, 255, 120, 109, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 123, 109, 77, 255, 121, 107, 239, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 236, 255, 122, 108, 71, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 109, 21, 255, 121, 107, 122, 255, 121, 107, 203, 255, 120, 107, 238, 255, 120, 107, 238, 255, 120, 107, 202, 255, 120, 107, 119, 255, 128, 115, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 232, 151, 12, 11, 242, 151, 12, 11, 250, 151, 12, 11, 254, 151, 12, 11, 254, 151, 12, 11, 250, 151, 12, 11, 242, 151, 12, 10, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 238, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 237, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 10, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 232, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 240, 151, 12, 10, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 234, 151, 12, 11, 241, 151, 12, 11, 255, 151, 12, 11, 253, 151, 11, 10, 232, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 242, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 10, 234, 151, 12, 11, 253, 151, 12, 11, 253, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 241, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 250, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 10, 234, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 250, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 10, 234, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 10, 234, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 250, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 250, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 242, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 253, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 10, 241, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 232, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 241, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 234, 151, 12, 11, 241, 151, 12, 11, 255, 151, 12, 11, 253, 151, 11, 10, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 237, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 237, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 232, 151, 12, 11, 242, 151, 12, 11, 250, 151, 12, 11, 253, 151, 12, 11, 253, 151, 12, 11, 250, 151, 12, 11, 241, 151, 11, 10, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_2rpr0"] -image = SubResource("Image_lpjla") +[sub_resource type="ImageTexture" id="ImageTexture_suo5c"] +image = SubResource("Image_0oden") -[sub_resource type="Image" id="Image_bq8kn"] +[sub_resource type="Image" id="Image_ipq44"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 178, 224, 224, 224, 194, 230, 230, 230, 20, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -120,9 +120,9 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_1oriu"] -image = SubResource("Image_bq8kn") +image = SubResource("Image_ipq44") -[sub_resource type="Image" id="Image_bwbka"] +[sub_resource type="Image" id="Image_d5kq4"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 195, 231, 231, 231, 21, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 195, 224, 224, 224, 178, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -132,55 +132,43 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_ikyhk"] -image = SubResource("Image_bwbka") +image = SubResource("Image_d5kq4") -[sub_resource type="Image" id="Image_8lbfl"] +[sub_resource type="Image" id="Image_8d0da"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 198, 222, 242, 147, 197, 222, 250, 147, 197, 222, 254, 147, 197, 222, 254, 147, 197, 222, 250, 147, 198, 222, 242, 147, 198, 223, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 238, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 198, 222, 253, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 198, 222, 253, 147, 197, 222, 255, 147, 198, 222, 240, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 198, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 232, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 241, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 198, 222, 241, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 197, 222, 253, 147, 197, 222, 255, 147, 198, 222, 241, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 197, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 231, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 198, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 232, 147, 198, 222, 242, 147, 198, 222, 250, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 250, 147, 197, 222, 241, 147, 198, 223, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_i2d73"] -image = SubResource("Image_8lbfl") - -[sub_resource type="Image" id="Image_ki3oo"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 178, 224, 224, 224, 194, 230, 230, 230, 20, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 223, 232, 147, 197, 222, 242, 147, 197, 222, 250, 147, 197, 222, 254, 147, 197, 222, 254, 147, 197, 222, 250, 147, 197, 222, 242, 147, 197, 222, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 238, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 232, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 240, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 197, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 232, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 0, 0, 0, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 241, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 0, 0, 0, 0, 147, 198, 222, 234, 147, 198, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 198, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 241, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 232, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 241, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 234, 147, 197, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 221, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 237, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 232, 147, 197, 222, 242, 147, 197, 222, 250, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 250, 147, 197, 222, 241, 147, 197, 222, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_mph2m"] -image = SubResource("Image_ki3oo") +[sub_resource type="ImageTexture" id="ImageTexture_qagbu"] +image = SubResource("Image_8d0da") -[sub_resource type="Image" id="Image_ivm1h"] +[sub_resource type="Image" id="Image_oy0ff"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 195, 231, 231, 231, 21, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 195, 224, 224, 224, 178, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 237, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 254, 58, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 254, 58, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 247, 171, 255, 57, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 58, 232, 170, 254, 57, 232, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 170, 254, 58, 232, 170, 253, 57, 251, 170, 253, 57, 251, 170, 254, 58, 236, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 170, 254, 58, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 170, 254, 57, 232, 170, 253, 57, 251, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 170, 254, 58, 242, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 57, 232, 170, 253, 57, 251, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 57, 232, 170, 253, 57, 244, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 57, 237, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 252, 170, 254, 57, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_k6fqi"] -image = SubResource("Image_ivm1h") +[sub_resource type="ImageTexture" id="ImageTexture_a4rkr"] +image = SubResource("Image_oy0ff") -[sub_resource type="Image" id="Image_uqb0l"] +[sub_resource type="Image" id="Image_iahim"] data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 144, 239, 151, 76, 142, 239, 151, 228, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 143, 239, 152, 229, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 244, 153, 45, 143, 239, 152, 175, 149, 255, 170, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 130, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 144, 244, 155, 23, 151, 244, 151, 22, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 144, 244, 155, 23, 143, 239, 151, 213, 142, 239, 152, 212, 145, 240, 152, 67, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 151, 244, 151, 22, 142, 239, 152, 212, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 142, 240, 152, 128, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 146, 243, 158, 21, 143, 239, 151, 211, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 146, 243, 158, 21, 143, 239, 152, 141, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 228, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 151, 225, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 143, 241, 154, 73, 142, 239, 151, 226, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 151, 225, 142, 241, 153, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 237, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 247, 130, 141, 130, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 130, 140, 131, 232, 129, 140, 130, 232, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 130, 140, 131, 232, 129, 139, 130, 251, 129, 139, 130, 251, 129, 139, 130, 236, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 129, 139, 130, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 129, 140, 130, 232, 129, 139, 130, 251, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 129, 139, 130, 242, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 130, 140, 130, 232, 129, 139, 130, 251, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 140, 130, 232, 129, 139, 130, 244, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 139, 130, 237, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 252, 129, 139, 130, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_04e57"] -image = SubResource("Image_uqb0l") +[sub_resource type="ImageTexture" id="ImageTexture_idt7c"] +image = SubResource("Image_iahim") [node name="StatusBar" type="PanelContainer"] clip_contents = true @@ -195,6 +183,7 @@ script = ExtResource("3") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 2 +size_flags_vertical = 0 [node name="tree_tools" type="HBoxContainer" parent="VBoxContainer"] layout_mode = 2 @@ -203,7 +192,7 @@ size_flags_vertical = 0 [node name="Label" type="Label" parent="VBoxContainer/tree_tools"] layout_mode = 2 size_flags_horizontal = 0 -text = "Statisitics" +text = "Statistics" [node name="tree_buttons" type="HBoxContainer" parent="VBoxContainer/tree_tools"] layout_mode = 2 @@ -218,212 +207,271 @@ layout_mode = 2 unique_name_in_owner = true layout_mode = 2 tooltip_text = "Run discover tests." -disabled = true -icon = SubResource("ImageTexture_jvn24") +icon = SubResource("ImageTexture_wo03e") [node name="btn_tree_sort" type="MenuButton" parent="VBoxContainer/tree_tools/tree_buttons"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Sets tree sorting mode." -disabled = true -icon = SubResource("ImageTexture_k82x4") +icon = SubResource("ImageTexture_c80wp") flat = false item_count = 4 popup/item_0/text = "Unsorted" -popup/item_0/icon = SubResource("ImageTexture_bs7qq") +popup/item_0/icon = SubResource("ImageTexture_t2qd7") popup/item_0/checkable = 1 +popup/item_0/id = 0 popup/item_1/text = "Name ascending" -popup/item_1/icon = SubResource("ImageTexture_k82x4") +popup/item_1/icon = SubResource("ImageTexture_c80wp") popup/item_1/checkable = 1 popup/item_1/checked = true popup/item_1/id = 1 popup/item_2/text = "Name descending" -popup/item_2/icon = SubResource("ImageTexture_0ck6a") +popup/item_2/icon = SubResource("ImageTexture_1mh1t") popup/item_2/checkable = 1 popup/item_2/id = 2 popup/item_3/text = "Execution time" -popup/item_3/icon = SubResource("ImageTexture_t7ac1") +popup/item_3/icon = SubResource("ImageTexture_bq8kn") popup/item_3/checkable = 1 popup/item_3/id = 3 [node name="btn_tree_mode" type="MenuButton" parent="VBoxContainer/tree_tools/tree_buttons"] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "Sets tree presentaion mode." -disabled = true -icon = SubResource("ImageTexture_03vfp") +tooltip_text = "Sets tree presentation mode." +icon = SubResource("ImageTexture_8lbfl") flat = false item_count = 2 popup/item_0/text = "Tree" -popup/item_0/icon = SubResource("ImageTexture_fv3i4") +popup/item_0/icon = SubResource("ImageTexture_ivm1h") popup/item_0/checkable = 1 popup/item_0/checked = true +popup/item_0/id = 0 popup/item_1/text = "Flat" -popup/item_1/icon = SubResource("ImageTexture_ab51p") +popup/item_1/icon = SubResource("ImageTexture_j00vj") popup/item_1/checkable = 1 popup/item_1/id = 1 [node name="HSeparator" type="HSeparator" parent="VBoxContainer"] layout_mode = 2 +theme_override_constants/separation = 0 [node name="status_bar" type="HFlowContainer" parent="VBoxContainer"] +layout_direction = 2 layout_mode = 2 -last_wrap_alignment = 1 +size_flags_vertical = 2 -[node name="errors" type="HBoxContainer" parent="VBoxContainer/status_bar"] +[node name="error" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) layout_mode = 2 -size_flags_vertical = 4 +size_flags_vertical = 0 +theme_override_constants/separation = -2 -[node name="error_value" type="Label" parent="VBoxContainer/status_bar/errors"] -unique_name_in_owner = true -use_parent_material = true -custom_minimum_size = Vector2(24, 0) +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/error"] layout_mode = 2 -size_flags_horizontal = 2 -text = "0" -horizontal_alignment = 2 -justification_flags = 0 +size_flags_horizontal = 3 -[node name="icon_errors" type="TextureRect" parent="VBoxContainer/status_bar/errors"] +[node name="icon_errors" type="TextureRect" parent="VBoxContainer/status_bar/error/icon"] unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 -texture = SubResource("ImageTexture_2rpr0") -stretch_mode = 2 +tooltip_text = "Error Tests" +texture = SubResource("ImageTexture_suo5c") +stretch_mode = 3 -[node name="Label" type="Label" parent="VBoxContainer/status_bar/errors"] +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/error/icon"] layout_mode = 2 -text = "Errors" -justification_flags = 0 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Jump to the previous error test" +icon = SubResource("ImageTexture_1oriu") -[node name="navigation" type="HBoxContainer" parent="VBoxContainer/status_bar/errors"] +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/error"] auto_translate_mode = 2 layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 localize_numeral_system = false -[node name="btn_error_up" type="Button" parent="VBoxContainer/status_bar/errors/navigation"] +[node name="error_value" type="Label" parent="VBoxContainer/status_bar/error/counter"] +unique_name_in_owner = true +use_parent_material = true +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_1oriu") +size_flags_horizontal = 10 +text = "0" +horizontal_alignment = 2 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 -[node name="btn_error_down" type="Button" parent="VBoxContainer/status_bar/errors/navigation"] +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/error/counter"] layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next error test" icon = SubResource("ImageTexture_ikyhk") [node name="VSeparator" type="VSeparator" parent="VBoxContainer/status_bar"] layout_mode = 2 -[node name="failures" type="HBoxContainer" parent="VBoxContainer/status_bar"] +[node name="failure" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) layout_mode = 2 -size_flags_vertical = 4 +theme_override_constants/separation = -2 -[node name="failure_value" type="Label" parent="VBoxContainer/status_bar/failures"] -unique_name_in_owner = true -use_parent_material = true -custom_minimum_size = Vector2(24, 0) +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/failure"] layout_mode = 2 -size_flags_horizontal = 0 -text = "0" -horizontal_alignment = 2 -vertical_alignment = 1 -justification_flags = 160 -max_lines_visible = 1 +size_flags_horizontal = 3 -[node name="icon_failures" type="TextureRect" parent="VBoxContainer/status_bar/failures"] +[node name="icon_failures" type="TextureRect" parent="VBoxContainer/status_bar/failure/icon"] unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 -texture = SubResource("ImageTexture_i2d73") -stretch_mode = 2 +tooltip_text = "Failed Tests" +texture = SubResource("ImageTexture_qagbu") +stretch_mode = 3 -[node name="Label" type="Label" parent="VBoxContainer/status_bar/failures"] +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/failure/icon"] layout_mode = 2 -text = "Failures" -justification_flags = 0 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Jump to the previous failed test" +icon = SubResource("ImageTexture_1oriu") -[node name="navigation" type="HBoxContainer" parent="VBoxContainer/status_bar/failures"] +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/failure"] auto_translate_mode = 2 layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 localize_numeral_system = false -[node name="btn_failure_up" type="Button" parent="VBoxContainer/status_bar/failures/navigation"] +[node name="failure_value" type="Label" parent="VBoxContainer/status_bar/failure/counter"] unique_name_in_owner = true +use_parent_material = true +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_mph2m") +size_flags_horizontal = 10 +text = "0" +horizontal_alignment = 2 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 -[node name="btn_failure_down" type="Button" parent="VBoxContainer/status_bar/failures/navigation"] -unique_name_in_owner = true +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/failure/counter"] layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_k6fqi") +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next failed test" +icon = SubResource("ImageTexture_ikyhk") [node name="VSeparator2" type="VSeparator" parent="VBoxContainer/status_bar"] layout_mode = 2 -[node name="flaky" type="HBoxContainer" parent="VBoxContainer/status_bar"] +[node name="flaky" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) +layout_mode = 2 +theme_override_constants/separation = -2 + +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/flaky"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="icon_flaky" type="TextureRect" parent="VBoxContainer/status_bar/flaky/icon"] +unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Flaky Tests" +texture = SubResource("ImageTexture_a4rkr") +stretch_mode = 3 + +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/flaky/icon"] +layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 +tooltip_text = "Jump to the previous flaky test" +icon = SubResource("ImageTexture_1oriu") + +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/flaky"] +auto_translate_mode = 2 +layout_mode = 2 +localize_numeral_system = false -[node name="flaky_value" type="Label" parent="VBoxContainer/status_bar/flaky"] +[node name="flaky_value" type="Label" parent="VBoxContainer/status_bar/flaky/counter"] unique_name_in_owner = true use_parent_material = true -custom_minimum_size = Vector2(24, 0) +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_horizontal = 0 +size_flags_horizontal = 10 text = "0" horizontal_alignment = 2 -vertical_alignment = 1 -justification_flags = 160 -max_lines_visible = 1 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 + +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/flaky/counter"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next flaky test" +icon = SubResource("ImageTexture_ikyhk") + +[node name="VSeparator3" type="VSeparator" parent="VBoxContainer/status_bar"] +layout_mode = 2 + +[node name="skipped" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) +layout_mode = 2 +theme_override_constants/separation = -2 + +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/skipped"] +layout_mode = 2 +size_flags_horizontal = 3 -[node name="icon_flaky" type="TextureRect" parent="VBoxContainer/status_bar/flaky"] +[node name="icon_skipped" type="TextureRect" parent="VBoxContainer/status_bar/skipped/icon"] unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 -texture = SubResource("ImageTexture_04e57") -stretch_mode = 2 +tooltip_text = "Skipped Tests" +texture = SubResource("ImageTexture_idt7c") +stretch_mode = 3 -[node name="Label" type="Label" parent="VBoxContainer/status_bar/flaky"] +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/skipped/icon"] layout_mode = 2 -text = "Flaky" -justification_flags = 0 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Jump to the previous skipped test" +icon = SubResource("ImageTexture_1oriu") -[node name="navigation" type="HBoxContainer" parent="VBoxContainer/status_bar/flaky"] +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/skipped"] auto_translate_mode = 2 layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 localize_numeral_system = false -[node name="btn_flaky_up" type="Button" parent="VBoxContainer/status_bar/flaky/navigation"] +[node name="skipped_value" type="Label" parent="VBoxContainer/status_bar/skipped/counter"] +unique_name_in_owner = true +use_parent_material = true +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_1oriu") +size_flags_horizontal = 10 +text = "0" +horizontal_alignment = 2 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 -[node name="btn_flaky_down" type="Button" parent="VBoxContainer/status_bar/flaky/navigation"] +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/skipped/counter"] layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next skipped test" icon = SubResource("ImageTexture_ikyhk") [connection signal="pressed" from="VBoxContainer/tree_tools/tree_buttons/btn_tree_sync" to="." method="_on_tree_sync_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/errors/navigation/btn_error_up" to="." method="_on_btn_error_up_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/errors/navigation/btn_error_down" to="." method="_on_btn_error_down_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/failures/navigation/btn_failure_up" to="." method="_on_failure_up_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/failures/navigation/btn_failure_down" to="." method="_on_failure_down_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/flaky/navigation/btn_flaky_up" to="." method="_on_btn_flaky_up_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/flaky/navigation/btn_flaky_down" to="." method="_on_btn_flaky_down_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/error/icon/btn_up" to="." method="_on_btn_error_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/error/counter/btn_down" to="." method="_on_btn_error_down_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/failure/icon/btn_up" to="." method="_on_failure_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/failure/counter/btn_down" to="." method="_on_failure_down_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/flaky/icon/btn_up" to="." method="_on_btn_flaky_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/flaky/counter/btn_down" to="." method="_on_btn_flaky_down_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/skipped/icon/btn_up" to="." method="_on_btn_skipped_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/skipped/counter/btn_down" to="." method="_on_btn_skipped_down_pressed"] diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid index fb517d53..296eea83 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid @@ -1 +1 @@ -uid://830jxlrpql1q +uid://dkq5g6l33hjlq diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn index 9031713f..5d75da02 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=22 format=3 uid="uid://dx7xy4dgi3wwb"] -[ext_resource type="Script" uid="uid://830jxlrpql1q" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd" id="3"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd" id="3"] [sub_resource type="Image" id="Image_c7rhl"] data = { diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd index fce16f98..536fe87f 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd +++ b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd @@ -64,24 +64,10 @@ const STATE = GdUnitInspectorTreeConstants.STATE var _tree_root: TreeItem var _current_selected_item: TreeItem = null -var _item_hash := Dictionary() var _current_tree_view_mode := GdUnitSettings.get_inspector_tree_view_mode() var _run_test_recovery := true -func _build_cache_key(resource_path: String, test_name: String) -> Array: - return [resource_path, test_name] - - -func add_tree_item_to_cache(resource_path: String, test_name: String, item: TreeItem) -> void: - var key := _build_cache_key(resource_path, test_name) - _item_hash[key] = item - - -func clear_tree_item_cache() -> void: - _item_hash.clear() - - ## Used for debugging purposes only func print_tree_item_ids(parent: TreeItem) -> TreeItem: for child in parent.get_children(): @@ -114,15 +100,16 @@ func _find_tree_item_by_id(parent: TreeItem, id: GdUnitGUID) -> TreeItem: return null -func _find_tree_item_by_path(resource_path: String, item_name: String) -> TreeItem: - var key := _build_cache_key(resource_path, item_name) - return _item_hash.get(key, null) - - -func _find_by_resource_path(current: TreeItem, resource_path: String) -> TreeItem: - for item in current.get_children(): - if get_item_source_file(item) == resource_path: - return item +func _find_tree_item_by_test_suite(parent: TreeItem, suite_path: String, suite_name: String) -> TreeItem: + for child in parent.get_children(): + if child.get_meta(META_GDUNIT_TYPE) == GdUnitType.TEST_SUITE: + var test_case: GdUnitTestCase = child.get_meta(META_TEST_CASE) + if test_case.suite_resource_path == suite_path and test_case.suite_name == suite_name: + return child + if child.get_child_count() > 0: + var item := _find_tree_item_by_test_suite(child, suite_path, suite_name) + if item != null: + return item return null @@ -222,7 +209,6 @@ func _ready() -> void: GdUnitSignals.instance().gdunit_test_discover_deleted.connect(on_test_case_discover_deleted) GdUnitSignals.instance().gdunit_test_discover_modified.connect(on_test_case_discover_modified) var command_handler := GdUnitCommandHandler.instance() - command_handler.gdunit_runner_start.connect(_on_gdunit_runner_start) command_handler.gdunit_runner_stop.connect(_on_gdunit_runner_stop) if _run_test_recovery: GdUnitTestDiscoverer.restore_last_session() @@ -262,7 +248,6 @@ func init_tree() -> void: func cleanup_tree() -> void: clear_reports() - clear_tree_item_cache() if not _tree_root: return _free_recursive() @@ -556,22 +541,31 @@ func set_state_initial(item: TreeItem, type: GdUnitType) -> void: set_item_icon_by_state(item) -func set_state_running(item: TreeItem) -> void: +func set_state_running(item: TreeItem, is_running: bool) -> void: if is_state_running(item): return - item.set_custom_color(0, Color.DARK_GREEN) - item.set_custom_color(1, Color.DARK_GREEN) - item.set_icon(0, ICON_SPINNER) - item.set_meta(META_GDUNIT_STATE, STATE.RUNNING) - item.collapsed = false + if is_item_state(item, STATE.INITIAL): + item.set_custom_color(0, Color.DARK_GREEN) + item.set_custom_color(1, Color.DARK_GREEN) + item.set_meta(META_GDUNIT_STATE, STATE.RUNNING) + item.collapsed = false + + if is_running: + item.set_icon(0, ICON_SPINNER) + else: + set_item_icon_by_state(item) + for child in item.get_children(): + set_item_icon_by_state(child) + var parent := item.get_parent() if parent != _tree_root: - set_state_running(parent) - # force scrolling to current test case - _tree.scroll_to_item(item, true) + set_state_running(parent, is_running) func set_state_succeded(item: TreeItem) -> void: + # Do not overwrite higher states + if is_state_error(item) or is_state_failed(item): + return if item == _tree_root: return item.set_custom_color(0, Color.GREEN) @@ -676,12 +670,12 @@ func update_state(item: TreeItem, event: GdUnitEvent, add_reports := true) -> vo if item == null: return - if event.is_success() and event.is_flaky(): + if event.is_skipped(): + set_state_skipped(item) + elif event.is_success() and event.is_flaky(): set_state_flaky(item, event) elif event.is_success(): set_state_succeded(item) - elif event.is_skipped(): - set_state_skipped(item) elif event.is_error(): set_state_error(item) elif event.is_failed(): @@ -693,6 +687,14 @@ func update_state(item: TreeItem, event: GdUnitEvent, add_reports := true) -> vo add_report(item, report) set_state_orphan(item, event) + var parent := item.get_parent() + if parent == null: + return + + var item_state: int = item.get_meta(META_GDUNIT_STATE) + var parent_state: int = parent.get_meta(META_GDUNIT_STATE) + if item_state <= parent_state: + return update_state(item.get_parent(), event, false) @@ -711,10 +713,6 @@ func abort_running(items:=_tree_root.get_children()) -> void: abort_running(item.get_children()) -func select_first_failure() -> TreeItem: - return select_item(_find_first_item_by_state(_tree_root, STATE.FAILED)) - - func _on_select_next_item_by_state(item_state: int) -> TreeItem: var current_selected := _tree.get_selected() # If nothing is selected, the first error is selected or the next one in the vicinity of the current selection is found @@ -765,17 +763,14 @@ func show_failed_report(selected_item: TreeItem) -> void: func update_test_suite(event: GdUnitEvent) -> void: - var item := _find_tree_item_by_path(extract_resource_path(event), event.suite_name()) + var item := _find_tree_item_by_test_suite(_tree_root, event.resource_path(), event.suite_name()) if not item: - push_error("[InspectorTreeMainPanel.gd:753] Internal Error: Can't find tree item for\n %s" % event) - return - if event.type() == GdUnitEvent.TESTSUITE_BEFORE: - set_state_running(item) + push_error("[InspectorTreeMainPanel#update_test_suite] Internal Error: Can't find test suite item '{_suite_name}' for {_resource_path} ".format(event)) return if event.type() == GdUnitEvent.TESTSUITE_AFTER: update_item_elapsed_time_counter(item, event.elapsed_time()) update_state(item, event) - update_progress_counters(item, 23) + set_state_running(item, false) func update_test_case(event: GdUnitEvent) -> void: @@ -784,7 +779,9 @@ func update_test_case(event: GdUnitEvent) -> void: #push_error("Internal Error: Can't find test id %s" % [event.guid()]) return if event.type() == GdUnitEvent.TESTCASE_BEFORE: - set_state_running(item) + set_state_running(item, true) + # force scrolling to current test case + _tree.scroll_to_item(item, true) return if event.type() == GdUnitEvent.TESTCASE_AFTER: @@ -792,7 +789,7 @@ func update_test_case(event: GdUnitEvent) -> void: if event.is_success() or event.is_warning(): update_item_processed_counter(item) update_state(item, event) - update_progress_counters(item, event.retry_count()) + update_progress_counters(item) func create_item(parent: TreeItem, test: GdUnitTestCase, item_name: String, type: GdUnitType) -> TreeItem: @@ -809,8 +806,6 @@ func create_item(parent: TreeItem, test: GdUnitTestCase, item_name: String, type GdUnitType.TEST_SUITE: # We need to create a copy of the test record meta with a new uniqe guid item.set_meta(META_TEST_CASE, GdUnitTestCase.from(test.suite_resource_path, test.source_file, test.line_number, test.suite_name)) - # We need to add the suite item to the item cache by path because the guid is not provided - add_tree_item_to_cache(test.source_file, item_name, item) item.set_meta(META_GDUNIT_NAME, item_name) set_state_initial(item, type) @@ -838,12 +833,6 @@ func update_item_total_counter(item: TreeItem) -> void: item.set_meta(META_GDUNIT_PROGRESS_COUNT_MAX, child_count) item.set_text(0, "(0/%d) %s" % [child_count, item.get_meta(META_GDUNIT_NAME)]) - if item == _tree_root: - var index: int = item.get_meta(META_GDUNIT_PROGRESS_INDEX) - var total_test: int = item.get_meta(META_GDUNIT_PROGRESS_COUNT_MAX) - var state: STATE = item.get_meta(META_GDUNIT_STATE) - test_counters_changed.emit(index, total_test, state) - update_item_total_counter(item.get_parent()) @@ -866,13 +855,9 @@ func update_item_processed_counter(item: TreeItem, add_count := 1) -> void: update_item_processed_counter(item.get_parent(), add_count) -func update_progress_counters(item: TreeItem, rety_count: int) -> void: - var index: int = _tree_root.get_meta(META_GDUNIT_PROGRESS_INDEX) +func update_progress_counters(item: TreeItem) -> void: + var index: int = _tree_root.get_meta(META_GDUNIT_PROGRESS_INDEX) + 1 var total_test: int = _tree_root.get_meta(META_GDUNIT_PROGRESS_COUNT_MAX) - # We only increment the index counter once for a test - if rety_count <= 1: - index += 1 - var state: STATE = item.get_meta(META_GDUNIT_STATE) test_counters_changed.emit(index, total_test, state) _tree_root.set_meta(META_GDUNIT_PROGRESS_INDEX, index) @@ -921,12 +906,6 @@ func recalculate_counters(parent: TreeItem) -> void: # Update the display text parent.set_text(0, "(%d/%d) %s" % [success_count, total_count, parent.get_meta(META_GDUNIT_NAME)]) - # If this is the root, emit the counter change signal - if parent == _tree_root: - var state: STATE = parent.get_meta(META_GDUNIT_STATE) - test_counters_changed.emit(progress_index, total_count, state) - - func update_item_elapsed_time_counter(item: TreeItem, time: int) -> void: item.set_text(1, "%s" % LocalTime.elapsed(time)) @@ -991,7 +970,7 @@ func get_icon_by_file_type(path: String, state: STATE, orphans: bool) -> Texture func on_test_case_discover_added(test_case: GdUnitTestCase) -> void: var test_root_folder := GdUnitSettings.test_root_folder().replace("res://", "") - var fully_qualified_name := test_case.fully_qualified_name.trim_prefix(test_root_folder).trim_suffix(test_case.display_name) + var fully_qualified_name := test_case.fully_qualified_name.trim_suffix(test_case.display_name) var parts := fully_qualified_name.split(".", false) parts.append(test_case.display_name) # Skip tree structure until test root folder @@ -1008,19 +987,22 @@ func on_test_case_discover_added(test_case: GdUnitTestCase) -> void: func create_items_tree_mode_tree(test_case: GdUnitTestCase, parts: PackedStringArray) -> void: var parent := _tree_root + var is_suite_assigned := false + var suite_name := test_case.suite_name.split(".")[-1] for item_name in parts: var next := _find_tree_item(parent, item_name) if next != null: parent = next continue - if item_name == test_case.display_name: + if not is_suite_assigned and suite_name == item_name: + next = create_item(parent, test_case, item_name, GdUnitType.TEST_SUITE) + is_suite_assigned = true + elif item_name == test_case.display_name: next = create_item(parent, test_case, item_name, GdUnitType.TEST_CASE) # On grouped tests (parameterized tests) elif item_name == test_case.test_name: next = create_item(parent, test_case, item_name, GdUnitType.TEST_GROUP) - elif test_case.suite_name.ends_with(item_name): - next = create_item(parent, test_case, item_name, GdUnitType.TEST_SUITE) else: next = create_item(parent, test_case, item_name, GdUnitType.FOLDER) parent = next @@ -1194,21 +1176,21 @@ func _on_Tree_item_activated() -> void: # external signal receiver ################################################################################ func _on_gdunit_runner_start() -> void: - reset_tree_state(_current_selected_item) _context_menu.set_item_disabled(CONTEXT_MENU_RUN_ID, true) _context_menu.set_item_disabled(CONTEXT_MENU_DEBUG_ID, true) + reset_tree_state(_tree_root) clear_reports() -func _on_gdunit_runner_stop(_client_id: int) -> void: +func _on_gdunit_runner_stop(_id: int) -> void: _context_menu.set_item_disabled(CONTEXT_MENU_RUN_ID, false) _context_menu.set_item_disabled(CONTEXT_MENU_DEBUG_ID, false) abort_running() sort_tree_items(_tree_root) # wait until the tree redraw await get_tree().process_frame - @warning_ignore("return_value_discarded") - select_first_failure() + var failure_item := _find_first_item_by_state(_tree_root, STATE.FAILED) + select_item( failure_item if failure_item else _current_selected_item) func _on_gdunit_event(event: GdUnitEvent) -> void: @@ -1226,12 +1208,7 @@ func _on_gdunit_event(event: GdUnitEvent) -> void: #_dump_tree_as_json("tree_example_discovered") GdUnitEvent.INIT: - reset_tree_state(_tree_root) - - GdUnitEvent.STOP: - sort_tree_items(_tree_root) - select_item(_current_selected_item) - #_dump_tree_as_json("tree_example") + _on_gdunit_runner_start() GdUnitEvent.TESTCASE_BEFORE: update_test_case(event) diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid index 268fc7f4..906971be 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid @@ -1 +1 @@ -uid://bh4nkqj4vy2uv +uid://cvwjxsbdokhhl diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn b/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn index 304443a0..a4247706 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=27 format=3 uid="uid://bqfpidewtpeg0"] -[ext_resource type="Script" uid="uid://bh4nkqj4vy2uv" path="res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd" id="1"] [sub_resource type="Image" id="Image_466oo"] data = { diff --git a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid index 94746596..a56ef416 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid +++ b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid @@ -1 +1 @@ -uid://b5tdsi1muqxwt +uid://d1xlwj0fjokwe diff --git a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn index c9caa11a..7e62c438 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn +++ b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://pmnkxrhglak5"] -[ext_resource type="Script" uid="uid://b5tdsi1muqxwt" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd" id="1_gki1u"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd" id="1_gki1u"] [node name="GdUnitInputMapper" type="Control"] modulate = Color(0.929099, 0.929099, 0.929099, 0.936189) diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid index a17cafd5..98262ab8 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid @@ -1 +1 @@ -uid://cp57pbdvfvm3o +uid://oxkc2vu7xdr diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn index aab370da..08afee43 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn @@ -1,11 +1,12 @@ -[gd_scene load_steps=8 format=3 uid="uid://dwgat6j2u77g4"] +[gd_scene load_steps=9 format=3] -[ext_resource type="Script" uid="uid://cp57pbdvfvm3o" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd" id="2"] -[ext_resource type="Texture2D" uid="uid://bkh022wwqq7s3" path="res://addons/gdUnit4/src/ui/settings/logo.png" id="3_isfyl"] -[ext_resource type="PackedScene" uid="uid://dte0m2endcgtu" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn" id="4"] -[ext_resource type="PackedScene" uid="uid://0xyeci1tqebj" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn" id="5_n1jtv"] -[ext_resource type="PackedScene" uid="uid://pmnkxrhglak5" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn" id="5_xu3j8"] -[ext_resource type="Script" uid="uid://dvgln5ecf7p7" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="8_2ggr0"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd" id="2"] +[ext_resource type="Texture2D" path="res://addons/gdUnit4/src/ui/settings/logo.png" id="3_isfyl"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn" id="4"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn" id="4_nf72w"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn" id="5_n1jtv"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn" id="5_xu3j8"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="8_2ggr0"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hbbq5"] content_margin_left = 10.0 @@ -226,10 +227,14 @@ layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 +[node name="Hooks" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("4_nf72w")] +visible = false +layout_mode = 2 + [node name="UI" type="ScrollContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties"] visible = false layout_mode = 2 -metadata/_tab_index = 1 +metadata/_tab_index = 2 [node name="ui-content" type="VBoxContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/UI"] unique_name_in_owner = true @@ -242,7 +247,7 @@ size_flags_vertical = 3 [node name="Shortcuts" type="ScrollContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties"] visible = false layout_mode = 2 -metadata/_tab_index = 2 +metadata/_tab_index = 3 [node name="shortcut-content" type="VBoxContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/Shortcuts"] unique_name_in_owner = true @@ -255,7 +260,7 @@ size_flags_vertical = 3 [node name="Report" type="ScrollContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties"] visible = false layout_mode = 2 -metadata/_tab_index = 3 +metadata/_tab_index = 4 [node name="report-content" type="VBoxContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/Report"] unique_name_in_owner = true @@ -268,13 +273,13 @@ size_flags_vertical = 3 [node name="Templates" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("4")] visible = false layout_mode = 2 -metadata/_tab_index = 4 +metadata/_tab_index = 5 [node name="Update" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("5_n1jtv")] unique_name_in_owner = true visible = false layout_mode = 2 -metadata/_tab_index = 5 +metadata/_tab_index = 6 [node name="GdUnitInputCapture" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("5_xu3j8")] unique_name_in_owner = true diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd new file mode 100644 index 00000000..3b01f340 --- /dev/null +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd @@ -0,0 +1,255 @@ +@tool +extends ScrollContainer + + +@onready var _hooks_tree: Tree = %hooks_tree +@onready var _hook_description: RichTextLabel = %hook_description +@onready var _btn_move_up: Button = %hook_actions/btn_move_up +@onready var _btn_move_down: Button = %hook_actions/btn_move_down +@onready var _btn_delete: Button = %hook_actions/btn_delete_hook +@onready var _select_hook_dlg: FileDialog = %select_hook_dlg +@onready var _error_msg_popup :AcceptDialog = %error_msg_popup + +var _selected_hook_item: TreeItem = null +var _root: TreeItem +var _system_box_style: StyleBoxFlat +var _priority_box_style: StyleBoxFlat + +func _ready() -> void: + _setup_styles() + _setup_buttons() + _setup_tree() + _load_registered_hooks() + + +func _setup_styles() -> void: + _system_box_style = StyleBoxFlat.new() + _system_box_style.bg_color = Color(1.0, 0.76, 0.03, 1) + _system_box_style.corner_radius_top_left = 6 + _system_box_style.corner_radius_top_right = 6 + _system_box_style.corner_radius_bottom_left = 6 + _system_box_style.corner_radius_bottom_right = 6 + _priority_box_style = _system_box_style.duplicate() + _priority_box_style.bg_color = Color(0.26, 0.54, 0.89, 1) + + +func _setup_buttons() -> void: + #if Engine.is_editor_hint(): + # _btn_move_up.icon = GdUnitUiTools.get_icon("MoveUp") + # _btn_move_down.icon = GdUnitUiTools.get_icon("MoveDown") + # _btn_add.icon = GdUnitUiTools.get_icon("Add") + # _btn_delete.icon = GdUnitUiTools.get_icon("Remove") + pass + + +func _setup_tree() -> void: + _hooks_tree.clear() + _root = _hooks_tree.create_item() + _hooks_tree.set_columns(2) + _hooks_tree.set_column_custom_minimum_width(1, 32) + _hooks_tree.set_column_expand(1, false) + _hooks_tree.set_hide_root(true) + _hooks_tree.set_hide_folding(true) + _hooks_tree.set_select_mode(Tree.SELECT_SINGLE) + _hooks_tree.item_selected.connect(_on_hook_selected) + _hooks_tree.item_edited.connect(_on_item_edited) + + +func _load_registered_hooks() -> void: + var hook_service := GdUnitTestSessionHookService.instance() + for hook: GdUnitTestSessionHook in hook_service.enigne_hooks: + _create_hook_tree_item(hook) + + # Select first item if any + if _root.get_child_count() > 0: + var first_item: TreeItem = _root.get_first_child() + first_item.select(0) + _on_hook_selected() + + +func _create_hook_tree_item(hook: GdUnitTestSessionHook) -> TreeItem: + var item: TreeItem = _hooks_tree.create_item(_root) + item.set_custom_minimum_height(26) + # Column 0: Hook info with custom drawing + item.set_cell_mode(0, TreeItem.CELL_MODE_CUSTOM) + item.set_custom_draw_callback(0, _draw_hook_item) + item.set_editable(0, false) + item.set_metadata(0, hook) + # Column 1: Checkbox for enable/disable + item.set_cell_mode(1, TreeItem.CELL_MODE_CHECK) + item.set_checked(1, GdUnitTestSessionHookService.is_enabled(hook)) + item.set_editable(1, true) + item.set_custom_bg_color(1, _hook_bg_color(hook)) + item.set_tooltip_text(1, "Enable/Disable the Hook") + item.propagate_check(1) + + if _is_system_hook(hook): + item.set_tooltip_text(0, "System hook - (Read-only)") + else: + item.set_tooltip_text(0, "User hook") + return item + + +func _hook_bg_color(hook: GdUnitTestSessionHook) -> Color: + if _is_system_hook(hook): + return Color(0.133, 0.118, 0.090, 1) # Brownish background for system hooks + return Color(0.176, 0.196, 0.235, 1) # Dark background #2d3142 + + +func _draw_hook_item(item: TreeItem, rect: Rect2) -> void: + var hook := _get_hook(item) + var is_system := _is_system_hook(hook) + var is_selected := item == _selected_hook_item + + # Draw background + var bg_color := _hook_bg_color(hook) # Dark background #2d3142 + if is_selected: + bg_color = bg_color.lerp(Color(0.2, 0.4, 0.6, 0.3), 0.5) # Blue tint for selection + _hooks_tree.draw_rect(rect, bg_color) + + # Draw left border for system hooks + if is_system: + var border_rect := Rect2(rect.position.x, rect.position.y, 3, rect.size.y) + _hooks_tree.draw_rect(border_rect, Color(1.0, 0.76, 0.03, 1)) # Yellow border + + var font := _hooks_tree.get_theme_default_font() + + # Draw hook name + var hook_name := hook.name + var text_pos := Vector2(rect.position.x + ( 15 if is_system else 12), rect.position.y + 18) + var text_color := Color(0.95, 0.95, 0.95, 1) + _hooks_tree.draw_string(font, text_pos, hook_name, HORIZONTAL_ALIGNMENT_LEFT, -1, 14, text_color) + + # Draw system badge if needed + if is_system: + var badge_x := rect.position.x + rect.end.x - 100 + var badge_y := rect.position.y + 14 + var system_badge_rect := Rect2(badge_x, badge_y-8, 48, 16) + _hooks_tree.draw_style_box(_system_box_style, system_badge_rect) + + var system_text_pos := Vector2(badge_x + 4, badge_y + 4) + var system_font_size := 10 + _hooks_tree.draw_string(font, system_text_pos, "SYSTEM", HORIZONTAL_ALIGNMENT_CENTER, -1, system_font_size, Color(0.1, 0.1, 0.1, 1)) + + +func _create_hook_display_text(hook_name: String, priority: int, is_system: bool) -> String: + var text := hook_name + "\n" + text += "Priority: [color=#4299e1][bgcolor=#4299e1] " + str(priority) + " [/bgcolor][/color]" + + if is_system: + text += " [color=#1a202c][bgcolor=#ffc107] SYSTEM [/bgcolor][/color]" + + return text + + +func _update_hook_description() -> void: + if _selected_hook_item == null: + _hook_description.text = "[i]Select a hook to view its description[/i]" + return + _hook_description.text = _get_hook(_selected_hook_item).description + + +func _update_hook_buttons() -> void: + # Is nothing selected disable the move and delete buttons + if _selected_hook_item == null: + _btn_move_up.disabled = true + _btn_move_down.disabled = true + _btn_delete.disabled = true + return + + var hook := _get_hook(_selected_hook_item) + var is_system := _is_system_hook(hook) + + # Disable the move and delete buttons for system hooks by default + if is_system: + _btn_move_up.disabled = true + _btn_move_down.disabled = true + _btn_delete.disabled = true + return + + var prev_item: TreeItem = _selected_hook_item.get_prev() + var next_item: TreeItem = _selected_hook_item.get_next() + + if prev_item != null: + var prev_hook := _get_hook(prev_item) + _btn_move_up.disabled = _is_system_hook(prev_hook) + + _btn_move_down.disabled = next_item == null + _btn_delete.disabled = false + + +static func _get_hook(item: TreeItem) -> GdUnitTestSessionHook: + return item.get_metadata(0) + + +static func _is_system_hook(hook: GdUnitTestSessionHook) -> bool: + if hook == null: + return false + return hook.get_meta("SYSTEM_HOOK") + + +func _on_hook_selected() -> void: + _selected_hook_item = _hooks_tree.get_selected() + _update_hook_buttons() + _update_hook_description() + + +func _on_item_edited() -> void: + var selected_hook_item := _hooks_tree.get_selected() + if selected_hook_item != null: + var hook := _get_hook(selected_hook_item) + var is_enabled := selected_hook_item.is_checked(1) + GdUnitTestSessionHookService.instance().enable_hook(hook, is_enabled) + + +func _on_btn_add_hook_pressed() -> void: + _select_hook_dlg.show() + + +func _on_select_hook_dlg_file_selected(path: String) -> void: + _select_hook_dlg.set_current_path(path) + _on_select_hook_dlg_confirmed() + + +func _on_select_hook_dlg_confirmed() -> void: + _select_hook_dlg.hide() + var result := GdUnitTestSessionHookService.instance().load_hook(_select_hook_dlg.get_current_path()) + if result.is_error(): + _error_msg_popup.dialog_text = result.error_message() + _error_msg_popup.show() + return + + var hook: GdUnitTestSessionHook = result.value() + result = GdUnitTestSessionHookService.instance().register(hook) + if result.is_error(): + _error_msg_popup.dialog_text = result.error_message() + _error_msg_popup.show() + return + + var hook_added := _create_hook_tree_item(hook) + _hooks_tree.set_selected(hook_added, 0) + + +func _on_btn_delete_hook_pressed() -> void: + if _selected_hook_item != null: + _root.remove_child(_selected_hook_item) + GdUnitTestSessionHookService.instance()\ + .unregister(_get_hook(_selected_hook_item)) + _selected_hook_item = null + _update_hook_buttons() + + +func _on_btn_move_up_pressed() -> void: + var prev := _selected_hook_item.get_prev() + _selected_hook_item.move_before(prev) + GdUnitTestSessionHookService.instance()\ + .move_before(_get_hook(_selected_hook_item), _get_hook(prev)) + _update_hook_buttons() + + +func _on_btn_move_down_pressed() -> void: + var next := _selected_hook_item.get_next() + _selected_hook_item.move_after(next) + GdUnitTestSessionHookService.instance()\ + .move_after(_get_hook(_selected_hook_item), _get_hook(next)) + _update_hook_buttons() diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd.uid b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd.uid new file mode 100644 index 00000000..273ecf35 --- /dev/null +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd.uid @@ -0,0 +1 @@ +uid://cs5mw8s67l61h diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn new file mode 100644 index 00000000..a9a850c0 --- /dev/null +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn @@ -0,0 +1,148 @@ +[gd_scene load_steps=10 format=3 uid="uid://41l7a46fol5m"] + +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd" id="1_8yffn"] + +[sub_resource type="Image" id="Image_h5sr5"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 255, 224, 224, 224, 255, 234, 234, 234, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 1, 225, 225, 225, 174, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 173, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 74, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 228, 228, 37, 224, 224, 224, 240, 224, 224, 224, 255, 224, 224, 224, 122, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 239, 228, 228, 228, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 200, 224, 224, 224, 255, 224, 224, 224, 172, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 1, 224, 224, 224, 173, 224, 224, 224, 255, 225, 225, 225, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 193, 234, 234, 234, 12, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 193, 224, 224, 224, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_77fm0"] +image = SubResource("Image_h5sr5") + +[sub_resource type="Image" id="Image_77fm0"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 193, 234, 234, 234, 12, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 193, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 200, 224, 224, 224, 255, 224, 224, 224, 173, 255, 255, 255, 1, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 1, 225, 225, 225, 174, 224, 224, 224, 255, 225, 225, 225, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 228, 228, 37, 224, 224, 224, 239, 224, 224, 224, 255, 224, 224, 224, 122, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 239, 227, 227, 227, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 74, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 1, 224, 224, 224, 173, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 255, 224, 224, 224, 255, 234, 234, 234, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_rewru"] +image = SubResource("Image_77fm0") + +[sub_resource type="Image" id="Image_kppp6"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_manhx"] +image = SubResource("Image_kppp6") + +[sub_resource type="Image" id="Image_rewru"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 227, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 73, 224, 224, 224, 226, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 225, 226, 226, 226, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_4h4u1"] +image = SubResource("Image_rewru") + +[node name="Hooks" type="ScrollContainer"] +custom_minimum_size = Vector2(400, 300) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_8yffn") +metadata/_tab_index = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="hooks_content" type="VBoxContainer" parent="HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="hooks_tree" type="Tree" parent="HBoxContainer/hooks_content"] +unique_name_in_owner = true +layout_direction = 2 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +hide_folding = true +hide_root = true + +[node name="hook_description" type="RichTextLabel" parent="HBoxContainer/hooks_content"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 120) +layout_mode = 2 +size_flags_vertical = 2 +bbcode_enabled = true +text = "The test result Html reporting hook." +scroll_active = false + +[node name="hook_actions" type="VBoxContainer" parent="HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(80, 0) +layout_mode = 2 +size_flags_horizontal = 0 +theme_override_constants/separation = 5 + +[node name="btn_move_up" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Move hook up in priority" +disabled = true +icon = SubResource("ImageTexture_77fm0") +icon_alignment = 1 + +[node name="btn_move_down" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Move hook down in priority" +disabled = true +icon = SubResource("ImageTexture_rewru") +icon_alignment = 1 + +[node name="btn_add_hook" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Add new hook" +icon = SubResource("ImageTexture_manhx") +icon_alignment = 1 + +[node name="btn_delete_hook" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Delete selected hook" +disabled = true +icon = SubResource("ImageTexture_4h4u1") +icon_alignment = 1 + +[node name="select_hook_dlg" type="FileDialog" parent="."] +unique_name_in_owner = true +disable_3d = true +title = "Open a File" +initial_position = 3 +current_screen = 0 +ok_button_text = "Open" +file_mode = 0 +filters = PackedStringArray("*.gd") + +[node name="error_msg_popup" type="AcceptDialog" parent="."] +unique_name_in_owner = true +initial_position = 3 +current_screen = 0 + +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_move_up" to="." method="_on_btn_move_up_pressed"] +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_move_down" to="." method="_on_btn_move_down_pressed"] +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_add_hook" to="." method="_on_btn_add_hook_pressed"] +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_delete_hook" to="." method="_on_btn_delete_hook_pressed"] +[connection signal="confirmed" from="select_hook_dlg" to="." method="_on_select_hook_dlg_confirmed"] +[connection signal="file_selected" from="select_hook_dlg" to="." method="_on_select_hook_dlg_file_selected"] diff --git a/addons/gdUnit4/src/ui/settings/logo.png.import b/addons/gdUnit4/src/ui/settings/logo.png.import index c2a071e8..aa273662 100644 --- a/addons/gdUnit4/src/ui/settings/logo.png.import +++ b/addons/gdUnit4/src/ui/settings/logo.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bkh022wwqq7s3" +uid="uid://wlkks3wwbw3p" path="res://.godot/imported/logo.png-deda0e4ba02a0b9e4e4a830029a5817f.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/logo.png-deda0e4ba02a0b9e4e4a830029a5817f.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid index 97233539..fa470ba1 100644 --- a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid +++ b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid @@ -1 +1 @@ -uid://ch427oehkvmwn +uid://6sigtj76qje5 diff --git a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn index 41724401..5ccc4430 100644 --- a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn +++ b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://dte0m2endcgtu"] -[ext_resource type="Script" uid="uid://ch427oehkvmwn" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd" id="1"] [node name="TestSuiteTemplate" type="MarginContainer"] anchors_preset = 15 diff --git a/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid b/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid index fabbbe0b..ed525731 100644 --- a/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid +++ b/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid @@ -1 +1 @@ -uid://dwgnucirrcvbe +uid://qgqmmr334y6v diff --git a/addons/gdUnit4/src/update/GdUnitPatch.gd.uid b/addons/gdUnit4/src/update/GdUnitPatch.gd.uid index f805425c..dcbc6fa9 100644 --- a/addons/gdUnit4/src/update/GdUnitPatch.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitPatch.gd.uid @@ -1 +1 @@ -uid://d2gxv8870ntjs +uid://b163bb15swk30 diff --git a/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid b/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid index 941d490e..5c341133 100644 --- a/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid @@ -1 +1 @@ -uid://dx0xkyyp5ib2d +uid://djandqnngk7to diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.gd b/addons/gdUnit4/src/update/GdUnitUpdate.gd index ef878151..97b7fdd1 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdate.gd +++ b/addons/gdUnit4/src/update/GdUnitUpdate.gd @@ -83,7 +83,7 @@ func run_update() -> void: await update_progress("Uninstall GdUnit4.") disable_gdUnit() if not _debug_mode: - delete_directory("res://addons/gdUnit4/") + GdUnitFileAccess.delete_directory("res://addons/gdUnit4/") # give editor time to react on deleted files await get_tree().create_timer(1).timeout @@ -96,11 +96,13 @@ func run_update() -> void: await update_progress("Patch invalid UID's") await patch_uids() + await rebuild_project() + await update_progress("New GdUnit version successfully installed, Restarting Godot please wait.") await get_tree().create_timer(3).timeout enable_gdUnit() hide() - delete_directory("res://addons/.gdunit_update") + GdUnitFileAccess.delete_directory("res://addons/.gdunit_update") restart_godot() @@ -187,37 +189,13 @@ func temp_dir() -> String: func create_temp_dir(folder_name :String) -> String: var new_folder := temp_dir() + "/" + folder_name - delete_directory(new_folder) + GdUnitFileAccess.delete_directory(new_folder) if not DirAccess.dir_exists_absolute(new_folder): @warning_ignore("return_value_discarded") DirAccess.make_dir_recursive_absolute(new_folder) return new_folder -func delete_directory(path: String, only_content := false) -> void: - var dir := DirAccess.open(path) - if dir != null: - @warning_ignore("return_value_discarded") - dir.list_dir_begin() - var file_name := "." - while file_name != "": - file_name = dir.get_next() - if file_name.is_empty() or file_name == "." or file_name == "..": - continue - var next := path + "/" +file_name - if dir.current_is_dir(): - delete_directory(next) - else: - # delete file - var err := dir.remove(next) - if err: - printerr("Delete %s failed: %s" % [next, error_string(err)]) - if not only_content: - var err := dir.remove(path) - if err: - printerr("Delete %s failed: %s" % [path, error_string(err)]) - - func copy_directory(from_dir: String, to_dir: String) -> bool: if not DirAccess.dir_exists_absolute(from_dir): printerr("Source directory not found '%s'" % from_dir) @@ -295,6 +273,26 @@ func download_release() -> void: await get_tree().create_timer(3).timeout +func rebuild_project() -> void: + # Check if this is a Godot .NET runtime instance + if not ClassDB.class_exists("CSharpScript"): + return + + update_progress("Rebuild the project ...") + await get_tree().process_frame + + var output := [] + var exit_code := OS.execute("dotnet", ["build"], output) + if exit_code == -1: + message_h4("Rebuild the project failed, check your project dependencies.", Color.INDIAN_RED) + await get_tree().create_timer(3).timeout + return + + for out: String in output: + print_rich("[color=DEEP_SKY_BLUE] %s" % out.strip_edges()) + await get_tree().process_frame + + func _on_confirmed() -> void: await run_update() diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid index de4bfc45..51b7e0cc 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid @@ -1 +1 @@ -uid://dy0foh4rxjst5 +uid://bkeembubt55db diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.tscn b/addons/gdUnit4/src/update/GdUnitUpdate.tscn index 9f6f2dd6..20d60b76 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdate.tscn +++ b/addons/gdUnit4/src/update/GdUnitUpdate.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://2eahgaw88y6q"] -[ext_resource type="Script" uid="uid://dy0foh4rxjst5" path="res://addons/gdUnit4/src/update/GdUnitUpdate.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdate.gd" id="1"] [sub_resource type="Gradient" id="Gradient_wilsr"] colors = PackedColorArray(0.151276, 0.151276, 0.151276, 1, 1, 1, 1, 1) diff --git a/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid index 4d88dee4..38f4d629 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid @@ -1 +1 @@ -uid://dvgln5ecf7p7 +uid://btsslcsmvikad diff --git a/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid index 0d7674e3..4dfee372 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid @@ -1 +1 @@ -uid://qnnsguq5143s +uid://82567pd8t2oq diff --git a/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn b/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn index 3add6384..46119f14 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn +++ b/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=4 format=3 uid="uid://0xyeci1tqebj"] -[ext_resource type="Script" uid="uid://qnnsguq5143s" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd" id="1_112wo"] -[ext_resource type="Script" uid="uid://dvgln5ecf7p7" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="2_18asx"] -[ext_resource type="PackedScene" uid="uid://2eahgaw88y6q" path="res://addons/gdUnit4/src/update/GdUnitUpdate.tscn" id="3_x87h6"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd" id="1_112wo"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="2_18asx"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/update/GdUnitUpdate.tscn" id="3_x87h6"] [node name="Control" type="MarginContainer"] anchors_preset = 15 diff --git a/addons/gdUnit4/src/update/assets/border_bottom.png.import b/addons/gdUnit4/src/update/assets/border_bottom.png.import index 70e9a028..d6fa62de 100644 --- a/addons/gdUnit4/src/update/assets/border_bottom.png.import +++ b/addons/gdUnit4/src/update/assets/border_bottom.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dmv3ld2otx1e2" +uid="uid://ce8mjo8om17du" path="res://.godot/imported/border_bottom.png-30d66a4c67e3a03ad191e37cdf16549d.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/border_bottom.png-30d66a4c67e3a03ad191e37cdf1 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/border_top.png.import b/addons/gdUnit4/src/update/assets/border_top.png.import index 814882aa..8cf1c385 100644 --- a/addons/gdUnit4/src/update/assets/border_top.png.import +++ b/addons/gdUnit4/src/update/assets/border_top.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4sio0j5om50s" +uid="uid://drjbcdxsbnkv1" path="res://.godot/imported/border_top.png-c47cbebdb755144731c6ae309e18bbaa.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/border_top.png-c47cbebdb755144731c6ae309e18bb compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/dot1.png.import b/addons/gdUnit4/src/update/assets/dot1.png.import index f01f7098..b4e1e069 100644 --- a/addons/gdUnit4/src/update/assets/dot1.png.import +++ b/addons/gdUnit4/src/update/assets/dot1.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ce2eojg0pwpov" +uid="uid://b3emxa2sotcgt" path="res://.godot/imported/dot1.png-380baf1b5247addda93bce3c799aa4e7.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/dot1.png-380baf1b5247addda93bce3c799aa4e7.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/dot2.png.import b/addons/gdUnit4/src/update/assets/dot2.png.import index f1e10178..8fa0a998 100644 --- a/addons/gdUnit4/src/update/assets/dot2.png.import +++ b/addons/gdUnit4/src/update/assets/dot2.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvwa5lg3qj0e2" +uid="uid://cstkvyuvs5ll7" path="res://.godot/imported/dot2.png-86a9db80ef4413e353c4339ad8f68a5f.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/dot2.png-86a9db80ef4413e353c4339ad8f68a5f.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/embedded.png.import b/addons/gdUnit4/src/update/assets/embedded.png.import index 26f37261..6184039d 100644 --- a/addons/gdUnit4/src/update/assets/embedded.png.import +++ b/addons/gdUnit4/src/update/assets/embedded.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://63wk5nib3r7q" +uid="uid://bo1eijiaooqmr" path="res://.godot/imported/embedded.png-29390948772209a603567d24f8766495.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/embedded.png-29390948772209a603567d24f8766495 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/horizontal-line2.png.import b/addons/gdUnit4/src/update/assets/horizontal-line2.png.import index 949745cf..2c0da4f1 100644 --- a/addons/gdUnit4/src/update/assets/horizontal-line2.png.import +++ b/addons/gdUnit4/src/update/assets/horizontal-line2.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dgaa5faajesgv" +uid="uid://b8prswatr643d" path="res://.godot/imported/horizontal-line2.png-92618e6ee5cc9002847547a8c9deadbc.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/horizontal-line2.png-92618e6ee5cc9002847547a8 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/godot_projectile_engine/GodotProjectileEngine.gd b/addons/godot_projectile_engine/GodotProjectileEngine.gd index 267f2e90..f92121b4 100644 --- a/addons/godot_projectile_engine/GodotProjectileEngine.gd +++ b/addons/godot_projectile_engine/GodotProjectileEngine.gd @@ -123,7 +123,7 @@ var projectile_wrapper_2d_nodes : Dictionary[String, Array] var projectile_composer_nodes : Dictionary[String, PatternComposer2D] var projectile_updater_2d_nodes : Dictionary[RID, ProjectileUpdater2D] -var projectile_node_manager_2d_nodes : Dictionary[StringName, ProjectileNodeManager2D] +var projectile_node_manager_2d_nodes : Dictionary[ProjectileTemplate2D, ProjectileNodeManager2D] var _projectile_count_temp : int @@ -374,10 +374,35 @@ func get_valid_target_group_nodes(_group_name: String) -> Array[Node2D]: return _valid_nodes -#endregion - +func get_collider_collision_layer(_collider: Node) -> int: + if !_collider: return 0 + if _collider is CollisionObject2D: + return _collider.collision_layer + elif _collider is TileMapLayer: + if !_collider.tile_set: + return 0 + var _tile_set : TileSet = _collider.tile_set + if _tile_set.get_physics_layers_count() <= 0 : + return 0 + for i in range(_tile_set.get_physics_layers_count()): + return _tile_set.get_physics_layer_collision_layer(i) + return 0 + +func get_collider_collision_mask(_collider: Node) -> int: + if _collider is CollisionObject2D: + return _collider.collision_mask + elif _collider is TileMapLayer: + if !_collider.tile_set: + return 0 + var _tile_set : TileSet = _collider.tile_set + if _tile_set.get_physics_layers_count() <= 0 : + return 0 + for i in range(_tile_set.get_physics_layers_count()): + return _tile_set.get_physics_layer_collision_mask(i) + return 0 +#endregion #endregion diff --git a/addons/godot_projectile_engine/core/pattern_composer/component/PCCSingle2D.gd b/addons/godot_projectile_engine/core/pattern_composer/component/PCCSingle2D.gd index 61e2b725..3577fdaf 100644 --- a/addons/godot_projectile_engine/core/pattern_composer/component/PCCSingle2D.gd +++ b/addons/godot_projectile_engine/core/pattern_composer/component/PCCSingle2D.gd @@ -67,7 +67,7 @@ func process_pattern( _pattern_composer_context: PatternComposerContext ) -> Array: - _new_pattern_composer_pack.clear() + _new_pattern_composer_pack = [] for _pattern_composer_data: PatternComposerData in _pattern_composer_pack: _new_pattern_composer_data = _pattern_composer_data.duplicate() _final_rotation = _pattern_composer_data.direction_rotation @@ -106,7 +106,7 @@ func process_pattern( continue DirectionType.MOUSE: - _pattern_composer_data.direction = _pattern_composer_data.position.direction_to(get_mouse_position()) + _new_pattern_composer_data.direction = _new_pattern_composer_data.position.direction_to(get_mouse_position()) _new_pattern_composer_pack.append(_new_pattern_composer_data) continue diff --git a/addons/godot_projectile_engine/core/pattern_composer/component/PCCSpread2D.gd b/addons/godot_projectile_engine/core/pattern_composer/component/PCCSpread2D.gd index 9e77a0ff..37e41ad8 100644 --- a/addons/godot_projectile_engine/core/pattern_composer/component/PCCSpread2D.gd +++ b/addons/godot_projectile_engine/core/pattern_composer/component/PCCSpread2D.gd @@ -35,7 +35,7 @@ func process_pattern( if spread_angle_random != Vector3.ZERO: spread_angle = ProjectileEngine.get_random_float_value(spread_angle_random) - _new_pattern_composer_pack.clear() + _new_pattern_composer_pack = [] match spread_type: SpreadType.STRAIGHT: for _pattern_composer_data: PatternComposerData in _pattern_composer_pack: @@ -58,41 +58,35 @@ var _point_position: Vector2 var _point_direction: Vector2 func _add_projectile_straight_spread(_pattern_composer_data: PatternComposerData) -> Array[PatternComposerData]: - _new_sub_pattern_composer_data.clear() + _new_sub_pattern_composer_data = [] _half_total_width = (spread_amount - 1) * spread_distance / 2.0 for i in range(spread_amount): _new_pattern_composer_data = _pattern_composer_data.duplicate() _offset_distance = (i * spread_distance) - _half_total_width - _new_pattern_composer_data.position += ( - _pattern_composer_data.direction.rotated(deg_to_rad(90) + _pattern_composer_data.direction_rotation) - * _offset_distance - ) + _new_pattern_composer_data.position += (_new_pattern_composer_data.direction * _offset_distance) _new_sub_pattern_composer_data.append(_new_pattern_composer_data) return _new_sub_pattern_composer_data func _add_projectile_angle_spread(_pattern_composer_data: PatternComposerData) -> Array[PatternComposerData]: - _new_sub_pattern_composer_data.clear() + _new_sub_pattern_composer_data = [] _half_total_deg = (spread_amount - 1) * spread_angle / 2.0 for i in range(spread_amount): _new_pattern_composer_data = _pattern_composer_data.duplicate() _offset_angle = (i * spread_angle) - _half_total_deg - _new_pattern_composer_data.direction_rotation += _pattern_composer_data.direction.angle() - deg_to_rad(_offset_angle) + _new_pattern_composer_data.direction = _pattern_composer_data.direction.rotated(deg_to_rad(_offset_angle)).normalized() _new_sub_pattern_composer_data.append(_new_pattern_composer_data) return _new_sub_pattern_composer_data func _add_projectile_hybrid_spread(_pattern_composer_data: PatternComposerData) -> Array[PatternComposerData]: - _new_sub_pattern_composer_data.clear() + _new_sub_pattern_composer_data = [] _half_total_width = (spread_amount - 1) * spread_distance / 2.0 _half_total_deg = (spread_amount - 1) * spread_angle / 2.0 for i in range(spread_amount): _new_pattern_composer_data = _pattern_composer_data.duplicate() _offset_distance = (i * spread_distance) - _half_total_width _offset_angle = (i * spread_angle) - _half_total_deg - _new_pattern_composer_data.position += ( - _pattern_composer_data.direction.rotated(deg_to_rad(90) + _pattern_composer_data.direction_rotation) - * _offset_distance - ) - _new_pattern_composer_data.direction_rotation += _pattern_composer_data.direction.angle() - deg_to_rad(_offset_angle) + _new_pattern_composer_data.direction = _pattern_composer_data.direction.rotated(deg_to_rad(_offset_angle)).normalized() + _new_pattern_composer_data.position += (_new_pattern_composer_data.direction * _offset_distance) _new_sub_pattern_composer_data.append(_new_pattern_composer_data) return _new_sub_pattern_composer_data diff --git a/addons/godot_projectile_engine/core/pattern_composer/pattern_composer.gd b/addons/godot_projectile_engine/core/pattern_composer/pattern_composer.gd index 084367cd..a010aa1c 100644 --- a/addons/godot_projectile_engine/core/pattern_composer/pattern_composer.gd +++ b/addons/godot_projectile_engine/core/pattern_composer/pattern_composer.gd @@ -51,9 +51,6 @@ func _physics_process(delta: float) -> void: ## Take a request pattern to process thorugh Pattern Composer Component to ## return a new Array[PatternComposerData] func request_pattern(_pattern_composer_context : PatternComposerContext) -> Array: - ## Init pattern composer pack - # _init_pattern_composer_data = PatternComposerData.new() - ## Check if can using ProjectileSpawnMarker2Ds _use_projectile_spawn_marker = false if _pattern_composer_context.use_spawn_markers and _pattern_composer_context.projectile_spawn_markers.size() > 0: for _projectile_spawn_marker in _pattern_composer_context.projectile_spawn_markers: @@ -77,6 +74,7 @@ func request_pattern(_pattern_composer_context : PatternComposerContext) -> Arra continue _new_composer_data = PatternComposerData.new() _new_composer_data.projectile_spawn_marker = _projectile_spawn_marker + _new_composer_data.direction = _projectile_spawn_marker.init_direction if _projectile_spawn_marker.use_global_position: _new_composer_data.position = _projectile_spawn_marker.global_position else: @@ -84,6 +82,7 @@ func request_pattern(_pattern_composer_context : PatternComposerContext) -> Arra _pattern_composer_spawner.append(_new_composer_data) else: for _composer_data in _pattern_composer_spawner: + _composer_data.direction = _composer_data.projectile_spawn_marker.init_direction if _composer_data.projectile_spawn_marker.use_global_position: _composer_data.position = _composer_data.projectile_spawn_marker.global_position else: diff --git a/addons/godot_projectile_engine/core/projectile_spawner/projectile_spawner.gd b/addons/godot_projectile_engine/core/projectile_spawner/projectile_spawner.gd index 78a6c563..689b443d 100644 --- a/addons/godot_projectile_engine/core/projectile_spawner/projectile_spawner.gd +++ b/addons/godot_projectile_engine/core/projectile_spawner/projectile_spawner.gd @@ -4,6 +4,7 @@ class_name ProjectileSpawner2D signal spawn_timed signal scheduler_completed +signal projectile_spawned(_projectile_template: ProjectileTemplate2D) @export var active : bool = true: set(value): @@ -18,6 +19,8 @@ signal scheduler_completed @export var timing_scheduler : TimingScheduler @export var use_spawn_markers : bool = false @export var audio_stream: AudioStreamPlayer +@export var audio_stream_2d: AudioStreamPlayer2D + var projectile_area : RID @@ -36,6 +39,7 @@ var _pattern_composer_pack : Array func _ready() -> void: + setup_spawn_marker() if active: activate_projectile_spawner() pass @@ -93,12 +97,12 @@ func setup_projectile_spawner() -> void: ProjectileTemplateNode2D: if !is_instance_valid( ProjectileEngine.projectile_node_manager_2d_nodes.get( - projectile_template_2d.projectile_2d_path + projectile_template_2d ) ): create_projectile_node_manager_2d() projectile_node_manager_2d = ProjectileEngine.projectile_node_manager_2d_nodes.get( - projectile_template_2d.projectile_2d_path + projectile_template_2d ) _: return @@ -119,26 +123,34 @@ func spawn_pattern() -> void: pattern_composer_context.position = global_position pattern_composer_context.projectile_template_2d = projectile_template_2d if use_spawn_markers: - setup_spawn_marker() pattern_composer_context.projectile_spawn_markers = projectile_spawn_markers else: pattern_composer_context.projectile_spawn_markers.clear() if typeof(projectile_template_2d) != TYPE_OBJECT: return + if !projectile_composer: + setup_projectile_spawner() + play_audio() match projectile_template_2d.get_script(): ProjectileTemplateNode2D: _pattern_composer_pack = projectile_composer.request_pattern(pattern_composer_context) projectile_node_manager_2d.spawn_projectile_pattern(_pattern_composer_pack) + projectile_spawned.emit(projectile_template_2d) ProjectileTemplateAdvanced2D: _pattern_composer_pack = projectile_composer.request_pattern(pattern_composer_context) projectile_updater_2d.spawn_projectile_pattern(_pattern_composer_pack) + projectile_spawned.emit(projectile_template_2d) + _: _pattern_composer_pack = projectile_composer.request_pattern(pattern_composer_context) projectile_updater_2d.spawn_projectile_pattern(_pattern_composer_pack) + projectile_spawned.emit(projectile_template_2d) + ## built-in classes don't have a script null: return + pass @@ -220,7 +232,7 @@ func create_projectile_node_manager_2d() -> void: ProjectileEngine.projectile_environment.add_child(_projectile_node_manager, true) _projectile_node_manager.owner = ProjectileEngine.projectile_environment ProjectileEngine.projectile_node_manager_2d_nodes.get_or_add( - projectile_template_2d.projectile_2d_path, _projectile_node_manager + projectile_template_2d, _projectile_node_manager ) _projectile_node_manager.setup_projectile_manager() pass @@ -254,36 +266,49 @@ func _spawn_projectile_template_node_2d() -> void: func connect_timing_scheduler() -> void: if !timing_scheduler: return + timing_scheduler.active = true if !timing_scheduler.scheduler_timed.is_connected(spawn_pattern): timing_scheduler.scheduler_timed.connect(spawn_pattern) - timing_scheduler.start_scheduler() pass func disconnect_timing_scheduler() -> void: if !timing_scheduler: return + timing_scheduler.active = false if timing_scheduler.scheduler_timed.is_connected(spawn_pattern): timing_scheduler.scheduler_timed.disconnect(spawn_pattern) - timing_scheduler.stop_scheduler() + # timing_scheduler.stop_scheduler() pass func play_audio() -> void: - if !audio_stream: return - audio_stream.playing = true + # audio_stream.playing = true + if audio_stream_2d: + print("play aduio") + audio_stream_2d.playing = true pass func connect_audio() -> void: - if !audio_stream: return - if !timing_scheduler.scheduler_timed.is_connected(play_audio): - timing_scheduler.scheduler_timed.connect(play_audio) + # if audio_stream_2d: + # projectile_spawned.connect(play_audio) + # if !timing_scheduler.scheduler_timed.is_connected(play_audio): + + # if !audio_stream: return + # if !timing_scheduler.scheduler_timed.is_connected(play_audio): + # timing_scheduler.scheduler_timed.connect(play_audio) + # if !audio_stream_2d: return + # if !timing_scheduler.scheduler_timed.is_connected(play_audio): + # timing_scheduler.scheduler_timed.connect(play_audio) pass func disconnect_audio() -> void: - if !audio_stream: return - if timing_scheduler.scheduler_timed.is_connected(play_audio): - timing_scheduler.scheduler_timed.disconnect(play_audio) + # if !audio_stream: return + # if timing_scheduler.scheduler_timed.is_connected(play_audio): + # timing_scheduler.scheduler_timed.disconnect(play_audio) + # if !audio_stream_2d: return + # if !timing_scheduler.scheduler_timed.is_connected(play_audio): + # timing_scheduler.scheduler_timed.disconnect(play_audio) pass diff --git a/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/destroy/behaviors/projectile_destroy_collision.gd b/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/destroy/behaviors/projectile_destroy_collision.gd index 9329b9c7..089dbe83 100644 --- a/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/destroy/behaviors/projectile_destroy_collision.gd +++ b/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/destroy/behaviors/projectile_destroy_collision.gd @@ -88,7 +88,8 @@ func process_behavior(_value, _context: Dictionary) -> bool: if destroy_on_body_collide: if _behavior_owner.has_overlapping_bodies(): for _overlap_body in _behavior_owner.get_overlapping_bodies(): - if not _overlap_body.collision_layer & _behavior_owner.collision_mask: + var _overlap_body_collision_layer : int = ProjectileEngine.get_collider_collision_layer(_overlap_body) + if not _overlap_body_collision_layer & _behavior_owner.collision_mask: continue if wait_projectile_piercing: @@ -128,8 +129,16 @@ func process_behavior(_value, _context: Dictionary) -> bool: var _projectile_updater : ProjectileUpdater2D = _behavior_owner.projectile_updater if destroy_on_area_collide: if _projectile_updater.has_overlapping_areas(_behavior_owner.area_index): + # print("ProjectileEngine ProjectileEngine: ", ProjectileEngine) for _overlap_area in _projectile_updater.get_overlapping_areas(_behavior_owner.area_index): - if not _overlap_area.collision_layer & _projectile_updater.projectile_collision_mask: + if !_overlap_area: + _projectile_updater.get_overlapping_areas(_behavior_owner.area_index).erase(_overlap_area) + return true + # print("ProjectileEngine ProjectileEngine: ", ProjectileEngine) + + var _overlap_area_collision_layer : int = ProjectileEngine.get_collider_collision_layer(_overlap_area) + + if not _overlap_area_collision_layer & _projectile_updater.projectile_collision_mask: continue if wait_projectile_piercing: @@ -168,7 +177,8 @@ func process_behavior(_value, _context: Dictionary) -> bool: if destroy_on_body_collide: if _projectile_updater.has_overlapping_bodies(_behavior_owner.area_index): for _overlap_body in _projectile_updater.get_overlapping_bodies(_behavior_owner.area_index): - if not _overlap_body.collision_layer & _projectile_updater.projectile_collision_mask: + var _overlap_body_collision_layer : int = ProjectileEngine.get_collider_collision_layer(_overlap_body) + if not _overlap_body_collision_layer & _projectile_updater.projectile_collision_mask: continue if wait_projectile_piercing: diff --git a/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/rotation/behaviors/projectile_rotation_follow_direction.gd b/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/rotation/behaviors/projectile_rotation_follow_direction.gd index 2fc74b18..b9cb1903 100644 --- a/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/rotation/behaviors/projectile_rotation_follow_direction.gd +++ b/addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/rotation/behaviors/projectile_rotation_follow_direction.gd @@ -19,5 +19,4 @@ func process_behavior(_value: float, _context: Dictionary) -> Dictionary: var _direction := _context.get(ProjectileEngine.BehaviorContext.DIRECTION) var _direction_rotation := _context.get(ProjectileEngine.BehaviorContext.DIRECTION_ROTATION) var _rotation_final : float = _direction.rotated(_direction_rotation).angle() - return {ProjectileEngine.RotationModify.ROTATION_OVERWRITE : _rotation_final} diff --git a/addons/godot_projectile_engine/core/projectile_template/base/projectile_updater/projectile_updater_2d.gd b/addons/godot_projectile_engine/core/projectile_template/base/projectile_updater/projectile_updater_2d.gd index 5747ed18..0466d01e 100644 --- a/addons/godot_projectile_engine/core/projectile_template/base/projectile_updater/projectile_updater_2d.gd +++ b/addons/godot_projectile_engine/core/projectile_template/base/projectile_updater/projectile_updater_2d.gd @@ -118,7 +118,6 @@ func _area_monitor_callback(status: int, area_rid : RID, instance_id: int, area_ return match status: PS.AREA_BODY_ADDED: - ProjectileEngine.projectile_instance_area_shape_entered.emit( projectile_instance_array[self_shape_idx], area_rid, _instance_node, area_shape_idx, @@ -153,7 +152,7 @@ func _area_monitor_callback(status: int, area_rid : RID, instance_id: int, area_ pass func _body_monitor_callback(status: int, body_rid : RID, instance_id: int, body_shape_idx: int, self_shape_idx: int) -> void: - var _instance_node : PhysicsBody2D = instance_from_id(instance_id) + var _instance_node : Node = instance_from_id(instance_id) if !is_instance_valid(_instance_node): return match status: @@ -224,7 +223,6 @@ func draw_projectile_texture() -> void: projectile_texture = projectile_template_2d.texture projectile_texture_modulate = projectile_template_2d.texture_modulate projectile_texture_draw_offset = Vector2.ZERO - projectile_template_2d.texture.get_size() * 0.5 - for index : int in projectile_active_index: draw_set_transform_matrix(projectile_instance_array[index].transform) draw_texture(projectile_texture, projectile_texture_draw_offset, projectile_texture_modulate) @@ -246,7 +244,8 @@ func get_active_projectile_count() -> int: ## Clear all ProjectileInstances in this ProjectileUpdater func clear_projectiles() -> void: for _index in range(projectile_max_pooling): - projectile_active_index.erase(_index) + if projectile_active_index.has(_index): + projectile_active_index.erase(_index) PS.area_set_shape_disabled(projectile_area_rid, _index, true) projectile_active_index.clear() pass diff --git a/addons/godot_projectile_engine/core/projectile_template/projectile_template_advanced_2d/projectile_updater_advanced_2d.gd b/addons/godot_projectile_engine/core/projectile_template/projectile_template_advanced_2d/projectile_updater_advanced_2d.gd index e17f44db..9b7fe0f0 100644 --- a/addons/godot_projectile_engine/core/projectile_template/projectile_template_advanced_2d/projectile_updater_advanced_2d.gd +++ b/addons/godot_projectile_engine/core/projectile_template/projectile_template_advanced_2d/projectile_updater_advanced_2d.gd @@ -2,44 +2,44 @@ extends ProjectileUpdater2D class_name ProjectileUpdaterAdvanced2D -var projectile_velocity : Vector2 = Vector2.ZERO - -var projectile_life_time_second_max : float = 10.0 -var projectile_life_distance_max : float = 300.0 - -var destroy_on_body_collide : bool -var destroy_on_area_collide : bool - -var projectile_speed_acceleration : float = 0.0 -var projectile_speed_max : float = 0.0 - -var projectile_is_use_homing : bool = false -var projectile_homing_target_group : String -var projetile_max_homing_distance : float -var projectile_steer_speed : float -var projectile_homing_strength : float -var _homing_group_nodes : Array[Node] -var _homing_nearest_target : Node2D -var _homing_nearest_distance : float -var _homing_distance : float -var _homing_target_position : Vector2 -var _homing_distance_to_target : float -var _homing_desired_direction : Vector2 -var _homing_new_direction : Vector2 -var _homing_final_direction : Vector2 - -var projectile_rotation_speed : float -var projectile_rotation_follow_direction : bool -var projectile_direction_follow_rotation : bool - -var projectile_scale_acceleration : float -var projectile_scale_max : Vector2 - -var projectile_is_use_trigger : bool -var projectile_trigger_name : StringName -var projectile_trigger_amount : int -var projectile_trigger_life_time : float -var projectile_trigger_life_distance : float +var projectile_velocity: Vector2 = Vector2.ZERO + +var projectile_life_time_second_max: float = 10.0 +var projectile_life_distance_max: float = 300.0 + +var destroy_on_body_collide: bool +var destroy_on_area_collide: bool + +var projectile_speed_acceleration: float = 0.0 +var projectile_speed_max: float = 0.0 + +var projectile_is_use_homing: bool = false +var projectile_homing_target_group: String +var projetile_max_homing_distance: float +var projectile_steer_speed: float +var projectile_homing_strength: float +var _homing_group_nodes: Array[Node] +var _homing_nearest_target: Node2D +var _homing_nearest_distance: float +var _homing_distance: float +var _homing_target_position: Vector2 +var _homing_distance_to_target: float +var _homing_desired_direction: Vector2 +var _homing_new_direction: Vector2 +var _homing_final_direction: Vector2 + +var projectile_rotation_speed: float +var projectile_rotation_follow_direction: bool +var projectile_direction_follow_rotation: bool + +var projectile_scale_acceleration: float +var projectile_scale_max: Vector2 + +var projectile_is_use_trigger: bool +var projectile_trigger_name: StringName +var projectile_trigger_amount: int +var projectile_trigger_life_time: float +var projectile_trigger_life_distance: float func init_updater_variable() -> void: @@ -49,8 +49,8 @@ func init_updater_variable() -> void: projectile_rotation_follow_direction = projectile_template_2d.rotation_follow_direction projectile_direction_follow_rotation = projectile_template_2d.direction_follow_rotation - projectile_life_time_second_max = projectile_template_2d.life_time_second_max - projectile_life_distance_max = projectile_template_2d.life_distance_max + projectile_life_time_second_max = projectile_template_2d.life_time_second_max + projectile_life_distance_max = projectile_template_2d.life_distance_max destroy_on_body_collide = projectile_template_2d.destroy_on_body_collide destroy_on_area_collide = projectile_template_2d.destroy_on_area_collide @@ -63,12 +63,12 @@ func spawn_projectile_pattern(pattern_composer_pack: Array[PatternComposerData]) projectile_rotation_follow_direction = projectile_template_2d.rotation_follow_direction projectile_direction_follow_rotation = projectile_template_2d.direction_follow_rotation - projectile_life_time_second_max = projectile_template_2d.life_time_second_max - projectile_life_distance_max = projectile_template_2d.life_distance_max + projectile_life_time_second_max = projectile_template_2d.life_time_second_max + projectile_life_distance_max = projectile_template_2d.life_distance_max destroy_on_body_collide = projectile_template_2d.destroy_on_body_collide destroy_on_area_collide = projectile_template_2d.destroy_on_area_collide - for _pattern_composer_data : PatternComposerData in pattern_composer_pack: + for _pattern_composer_data: PatternComposerData in pattern_composer_pack: _projectile_instance = projectile_instance_array[projectile_pooling_index] _projectile_instance = _projectile_instance as ProjectileInstanceAdvanced2D @@ -85,7 +85,7 @@ func spawn_projectile_pattern(pattern_composer_pack: Array[PatternComposerData]) _projectile_instance.direction_rotation_speed = deg_to_rad(projectile_template_2d.direction_rotation_speed) _projectile_instance.texture_rotation = projectile_template_2d.texture_rotation - _projectile_instance.texture_rotation_speed = deg_to_rad(projectile_template_2d.texture_rotation_speed) + _projectile_instance.texture_rotation_speed = deg_to_rad(projectile_template_2d.texture_rotation_speed) _projectile_instance.scale = projectile_template_2d.scale _projectile_instance.scale_acceleration = projectile_template_2d.scale_acceleration @@ -214,7 +214,6 @@ func update_projectile_instances(delta: float) -> void: # projectile_speed = projectile_template_2d.speed # projectile_speed_acceleration = projectile_template_2d.speed_acceleration # projectile_speed_max = projectile_template_2d.speed_max - projectile_is_use_homing = projectile_template_2d.is_use_homing if projectile_is_use_homing: projectile_homing_target_group = projectile_template_2d.target_group @@ -228,9 +227,10 @@ func update_projectile_instances(delta: float) -> void: projectile_trigger_life_time = projectile_template_2d.trigger_life_time projectile_trigger_life_distance = projectile_template_2d.trigger_life_distance + var _overlap_collision_layer: int # Check for projectile destroy condition - for index : int in projectile_active_index: + for index: int in projectile_active_index: _projectile_instance = projectile_instance_array[index] # Life Time & Distance @@ -249,20 +249,22 @@ func update_projectile_instances(delta: float) -> void: if destroy_on_area_collide: if has_overlapping_areas(index): for _overlap_area in get_overlapping_areas(index): - if not _overlap_area.collision_layer & projectile_collision_mask: + _overlap_collision_layer = ProjectileEngine.get_collider_collision_layer(_overlap_area) + if not _overlap_collision_layer & projectile_collision_mask: continue projectile_remove_index.append(index) if destroy_on_body_collide: if has_overlapping_bodies(index): for _overlap_body in get_overlapping_bodies(index): - if not _overlap_body.collision_layer & projectile_collision_mask: + _overlap_collision_layer = ProjectileEngine.get_collider_collision_layer(_overlap_body) + if not _overlap_collision_layer & projectile_collision_mask: continue projectile_remove_index.append(index) # Destroy projectile if projectile_remove_index.size() > 0: - for index : int in projectile_remove_index: + for index: int in projectile_remove_index: projectile_active_index.erase(index) if projectile_template_2d.collision_shape: PS.area_set_shape_disabled(projectile_area_rid, index, true) @@ -270,12 +272,11 @@ func update_projectile_instances(delta: float) -> void: # Update active projectile instances array _active_projectile_instances.clear() - for index : int in projectile_active_index: + for index: int in projectile_active_index: _active_projectile_instances.append(projectile_instance_array[index]) if _active_projectile_instances.size() <= 0: return # Update active projectile - for _active_projectile_instance : ProjectileInstanceAdvanced2D in _active_projectile_instances: - + for _active_projectile_instance: ProjectileInstanceAdvanced2D in _active_projectile_instances: if projectile_is_use_trigger: if _active_projectile_instance.trigger_count < projectile_trigger_amount: if projectile_trigger_life_time > 0: @@ -334,7 +335,7 @@ func update_projectile_instances(delta: float) -> void: ) if _active_projectile_instance.texture_rotation_speed != 0: - _active_projectile_instance.texture_rotation += _active_projectile_instance.texture_rotation_speed * delta + _active_projectile_instance.texture_rotation += _active_projectile_instance.texture_rotation_speed * delta if projectile_direction_follow_rotation: _active_projectile_instance.direction_rotation = _active_projectile_instance.texture_rotation diff --git a/addons/godot_projectile_engine/core/projectile_template/projectile_template_custom_2d/projectile_updater_custom_2d.gd b/addons/godot_projectile_engine/core/projectile_template/projectile_template_custom_2d/projectile_updater_custom_2d.gd index adda5966..159bd259 100644 --- a/addons/godot_projectile_engine/core/projectile_template/projectile_template_custom_2d/projectile_updater_custom_2d.gd +++ b/addons/godot_projectile_engine/core/projectile_template/projectile_template_custom_2d/projectile_updater_custom_2d.gd @@ -162,7 +162,7 @@ func spawn_projectile_pattern(pattern_composer_pack: Array[PatternComposerData]) if projectile_pooling_index >= projectile_max_pooling: projectile_pooling_index = 0 - # update_projectile_instances(get_physics_process_delta_time()) + update_projectile_instances(get_physics_process_delta_time()) #endregion @@ -544,6 +544,9 @@ func process_behavior_context_request( ProjectileEngine.BehaviorContext.DIRECTION: _behavior_context.get_or_add(_behavior_context_request, _projectile_instance.direction) + ProjectileEngine.BehaviorContext.DIRECTION_ROTATION: + _behavior_context.get_or_add(_behavior_context_request, _projectile_instance.direction_rotation) + ProjectileEngine.BehaviorContext.BASE_DIRECTION: _behavior_context.get_or_add(_behavior_context_request, _projectile_instance.base_direction) diff --git a/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_2d/projectile_2d.gd b/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_2d/projectile_2d.gd index 08f44905..a3533d34 100644 --- a/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_2d/projectile_2d.gd +++ b/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_2d/projectile_2d.gd @@ -5,86 +5,94 @@ class_name Projectile2D signal projectile_pierced(projectile_node: Projectile2D, pierced_node: Node2D) signal projectile_instance_pierced(projectile_node: ProjectileInstance2D, pierced_node: Node2D) -@export var active : bool = false -@export var speed : float = 100 -@export var direction : Vector2 = Vector2.RIGHT -@export_range(-360, 360, 0.1, "radians_as_degrees", "suffix:°") var texture_rotation : float -@export var collision_shape : CollisionShape2D +@export var active: bool = false +@export var speed: float = 100 +@export var direction: Vector2 = Vector2.RIGHT +@export_range(-360, 360, 0.1, "radians_as_degrees", "suffix:°") var texture_rotation: float +@export var collision_shape: CollisionShape2D # @export var pooling_amount : int = 200 @export_group("Projectile Behavior") @export_subgroup("Transform") -@export var speed_projectile_behaviors : Array[ProjectileBehaviorSpeed] -@export var direction_projectile_behaviors : Array[ProjectileBehaviorDirection] -@export var rotation_projectile_behaviors : Array[ProjectileBehaviorRotation] -@export var scale_projectile_behaviors : Array[ProjectileBehaviorScale] +@export var speed_projectile_behaviors: Array[ProjectileBehaviorSpeed] +@export var direction_projectile_behaviors: Array[ProjectileBehaviorDirection] +@export var rotation_projectile_behaviors: Array[ProjectileBehaviorRotation] +@export var scale_projectile_behaviors: Array[ProjectileBehaviorScale] @export_subgroup("Special") -@export var destroy_projectile_behaviors : Array[ProjectileBehaviorDestroy] -@export var piercing_projectile_behaviors : Array[ProjectileBehaviorPiercing] -@export var bouncing_projectile_behaviors : Array[ProjectileBehaviorBouncing] -@export var trigger_projectile_behaviors : Array[ProjectileBehaviorTrigger] +@export var destroy_projectile_behaviors: Array[ProjectileBehaviorDestroy] +@export var piercing_projectile_behaviors: Array[ProjectileBehaviorPiercing] +@export var bouncing_projectile_behaviors: Array[ProjectileBehaviorBouncing] +@export var trigger_projectile_behaviors: Array[ProjectileBehaviorTrigger] @export_group("Random") @export var speed_random: Vector3 @export var texture_rotation_random: Vector3 @export var scale_random: Vector3 -var projectile_node_manager : ProjectileNodeManager2D -var projectile_node_index : int +var projectile_template_2d : ProjectileTemplate2D -var velocity : Vector2 +var projectile_node_manager: ProjectileNodeManager2D +var projectile_node_index: int = -1 + +var velocity: Vector2 var life_time_second: float -var life_distance : float +var life_distance: float -var base_speed : float -var speed_final : float -var _speed_addition : float -var _speed_multiply : float +var base_speed: float +var speed_final: float +var speed_clamp : Vector2 +var _speed_addition: float +var _speed_multiply: float # var behavior_values : Dictionary -var _speed_behavior_additions : Dictionary -var _speed_behavior_multiplies : Dictionary -var _speed_multiply_value : float - -var base_direction : Vector2 -var base_direction_rotation : float -var raw_direction : Vector2 -var direction_rotation : float -var direction_final : Vector2 -var _direction_behavior_values : Dictionary -var _direction_behavior_additions : Dictionary -var _direction_behavior_rotations : Dictionary -var _direction_rotation_value : float -var _direction_addition_value : Vector2 -var _direction_addition : Vector2 +var _speed_behavior_additions: Dictionary +var _speed_behavior_multiplies: Dictionary +var _base_speed_behavior_multiplies : Dictionary + +var _speed_multiply_value: float + +var base_direction: Vector2 +var base_direction_rotation: float +var raw_direction: Vector2 +var direction_rotation: float +var direction_final: Vector2 +var _direction_behavior_values: Dictionary +var _direction_behavior_additions: Dictionary +var _direction_behavior_rotations: Dictionary +var _direction_rotation_value: float +var _direction_addition_value: Vector2 +var _direction_addition: Vector2 # var projectile_rotation : float -var base_rotation : float -var rotation_final : float -var behavior_values : Dictionary -var _rotation_behavior_additions : Dictionary -var _rotation_behavior_multiplies : Dictionary -var _rotation_multiply_value : float -var _rotation_multiply : float -var _rotation_addition : float - -var projectile_scale : Vector2 -var base_scale : Vector2 -var scale_final : Vector2 +var base_rotation: float +var rotation_final: float +var behavior_values: Dictionary +var _rotation_behavior_additions: Dictionary +var _rotation_behavior_multiplies: Dictionary +var _rotation_multiply_value: float +var _rotation_multiply: float +var _rotation_addition: float + +var projectile_scale: Vector2 +var base_scale: Vector2 +var scale_final: Vector2 # var behavior_values : Dictionary -var _scale_behavior_additions : Dictionary -var _scale_behavior_multiplies : Dictionary -var _scale_multiply_value : Vector2 -var _scale_multiply : Vector2 -var _scale_addition : Vector2 - -var projectile_behavior_context : Dictionary -var _behavior_context_requests_normal : Array[ProjectileEngine.BehaviorContext] -var _behavior_contest_requests_persist : Array[ProjectileEngine.BehaviorContext] -var _normal_behavior_context : Dictionary -var _persist_behavior_context : Dictionary -var projectile_behaviors : Array[ProjectileBehavior] = [] +var _scale_behavior_additions: Dictionary +var _scale_behavior_multiplies: Dictionary +var _scale_multiply_value: Vector2 +var _scale_multiply: Vector2 +var _scale_addition: Vector2 + +var projectile_behavior_context: Dictionary +var _behavior_context_requests_normal: Array[ProjectileEngine.BehaviorContext] +var _behavior_contest_requests_persist: Array[ProjectileEngine.BehaviorContext] +var _normal_behavior_context: Dictionary +var _persist_behavior_context: Dictionary +var projectile_behaviors: Array[ProjectileBehavior] = [] + +var velocity_addition: Vector2 + func _set(property: StringName, value: Variant) -> bool: match property: @@ -122,6 +130,7 @@ func apply_pattern_composer_data(_pattern_composer_data: PatternComposerData) -> func setup_projectile_2d() -> void: + init_base_properties() setup_projectile_behavior() update_projectile_2d(get_physics_process_delta_time()) @@ -129,6 +138,7 @@ func setup_projectile_2d() -> void: func init_base_properties() -> void: + process_randomness() base_speed = speed base_direction = direction base_rotation = texture_rotation @@ -136,6 +146,15 @@ func init_base_properties() -> void: base_scale = scale projectile_scale = scale +func apply_custom_data() -> void: + if !projectile_template_2d: return + if projectile_template_2d.custom_data.size() <= 0: return + if !projectile_template_2d.custom_data[0] is Dictionary: return + for _key in projectile_template_2d.custom_data[0]: + # print(_key, " - ", projectile_template_2d.custom_data[0].get(_key)) + set(_key, projectile_template_2d.custom_data[0].get(_key)) + # print(get(_key)) + func setup_projectile_behavior() -> void: projectile_behaviors.clear() @@ -195,7 +214,7 @@ func update_projectile_2d(delta: float) -> void: continue if !_trigger_behavior.active: continue - var _trigger_behavior_values : Dictionary = _trigger_behavior.process_behavior(null, projectile_behavior_context) + var _trigger_behavior_values: Dictionary = _trigger_behavior.process_behavior(null, projectile_behavior_context) if _trigger_behavior_values.has("is_trigger"): if _trigger_behavior_values.is_trigger: ProjectileEngine.projectile_node_triggered.emit(_trigger_behavior.trigger_name, self) @@ -210,7 +229,7 @@ func update_projectile_2d(delta: float) -> void: if !_projectile_behavior.active: continue - var _piercing_behavior_values : Dictionary = _projectile_behavior.process_behavior(null, projectile_behavior_context) + var _piercing_behavior_values: Dictionary = _projectile_behavior.process_behavior(null, projectile_behavior_context) if _piercing_behavior_values.size() <= 0: continue @@ -230,10 +249,10 @@ func update_projectile_2d(delta: float) -> void: ProjectileEngine.projectile_environment.projectile_bouncing_helper.collision_layer = self.collision_layer ProjectileEngine.projectile_environment.projectile_bouncing_helper.collision_mask = self.collision_mask - var _bouncing_behavior_values : Dictionary = _projectile_behavior.process_behavior(null, projectile_behavior_context) + var _bouncing_behavior_values: Dictionary = _projectile_behavior.process_behavior(null, projectile_behavior_context) if _bouncing_behavior_values.size() <= 0: continue - if _bouncing_behavior_values.has("is_bouncing"): #and _bouncing_behavior_values.has(ProjectileEngine.DirectionModify.DIRECTION_OVERWRITE): + if _bouncing_behavior_values.has("is_bouncing"): # and _bouncing_behavior_values.has(ProjectileEngine.DirectionModify.DIRECTION_OVERWRITE): direction = _bouncing_behavior_values.get(ProjectileEngine.DirectionModify.DIRECTION_OVERWRITE) pass @@ -255,15 +274,28 @@ func update_projectile_2d(delta: float) -> void: continue if not _projectile_behavior.active: continue - behavior_values = _projectile_behavior.process_behavior(speed, projectile_behavior_context) + behavior_values = _projectile_behavior.process_behavior( + speed, + projectile_behavior_context + ) for _behavior_key in behavior_values.keys(): match _behavior_key: ProjectileEngine.SpeedModify.SPEED_OVERWRITE: speed = behavior_values.get(ProjectileEngine.SpeedModify.SPEED_OVERWRITE) ProjectileEngine.SpeedModify.SPEED_ADDITION: - _speed_behavior_additions.get_or_add(_projectile_behavior, behavior_values.get(ProjectileEngine.SpeedModify.SPEED_ADDITION)) + _speed_behavior_additions.get_or_add( + _projectile_behavior, behavior_values.get(ProjectileEngine.SpeedModify.SPEED_ADDITION) + ) ProjectileEngine.SpeedModify.SPEED_MULTIPLY: - _speed_behavior_multiplies.get_or_add(_projectile_behavior, behavior_values.get(ProjectileEngine.SpeedModify.SPEED_MULTIPLY)) + _speed_behavior_multiplies.get_or_add( + _projectile_behavior, behavior_values.get(ProjectileEngine.SpeedModify.SPEED_MULTIPLY) + ) + ProjectileEngine.SpeedModify.BASE_SPEED_MULTIPLY: + _base_speed_behavior_multiplies.get_or_add( + _projectile_behavior, behavior_values.get(ProjectileEngine.SpeedModify.BASE_SPEED_MULTIPLY) + ) + ProjectileEngine.SpeedModify.SPEED_CLAMP: + speed_clamp = behavior_values.get(ProjectileEngine.SpeedModify.SPEED_CLAMP) if direction_projectile_behaviors.size() > 0: _direction_behavior_rotations.clear() @@ -280,12 +312,12 @@ func update_projectile_2d(delta: float) -> void: direction = _direction_behavior_values.get(ProjectileEngine.DirectionModify.DIRECTION_OVERWRITE) ProjectileEngine.DirectionModify.DIRECTION_ROTATION: _direction_behavior_rotations.get_or_add( - _projectile_behavior, + _projectile_behavior, _direction_behavior_values.get(ProjectileEngine.DirectionModify.DIRECTION_ROTATION) ) ProjectileEngine.DirectionModify.DIRECTION_ADDITION: _direction_behavior_additions.get_or_add( - _projectile_behavior, + _projectile_behavior, _direction_behavior_values.get(ProjectileEngine.DirectionModify.DIRECTION_ADDITION) ) @@ -305,7 +337,7 @@ func update_projectile_2d(delta: float) -> void: ProjectileEngine.RotationModify.ROTATION_OVERWRITE) ProjectileEngine.RotationModify.ROTATION_ADDITION: _rotation_behavior_additions.get_or_add( - _projectile_behavior, + _projectile_behavior, behavior_values.get(ProjectileEngine.RotationModify.ROTATION_ADDITION)) if scale_projectile_behaviors.size() > 0: _scale_behavior_additions.clear() @@ -359,23 +391,30 @@ func update_projectile_2d(delta: float) -> void: _direction_rotation_value = 0 for _direction_behavior_rotation in _direction_behavior_rotations.values(): _direction_rotation_value += _direction_behavior_rotation - direction_rotation = base_direction_rotation + _direction_rotation_value + direction_rotation = base_direction_rotation + _direction_rotation_value direction_final = direction_final.rotated(direction_rotation).normalized() + ## Apply Projectile behaviors Speed speed_final = speed - if _speed_behavior_multiplies.size() > 0: - _speed_multiply_value = 0 - for _speed_behavior_multiply in _speed_behavior_multiplies.values(): - _speed_multiply_value += _speed_behavior_multiply - _speed_multiply = base_speed * _speed_multiply_value - speed_final += _speed_multiply if _speed_behavior_additions.size() > 0: _speed_addition = 0 for _speed_behavior_addition in _speed_behavior_additions.values(): _speed_addition += _speed_behavior_addition speed_final += _speed_addition + if _speed_behavior_multiplies.size() > 0: + _speed_multiply_value = 0 + for _speed_behavior_multiply in _speed_behavior_multiplies.values(): + _speed_multiply_value += _speed_behavior_multiply - 1.0 + _speed_multiply = speed * _speed_multiply_value + speed_final += _speed_multiply + if _base_speed_behavior_multiplies.size() > 0: + _speed_multiply_value = 0 + for _base_speed_behavior_multiply in _base_speed_behavior_multiplies.values(): + _speed_multiply_value += _base_speed_behavior_multiply + _speed_multiply = base_speed * _speed_multiply_value + speed_final += _speed_multiply - velocity = speed_final * direction_final * delta + velocity = speed_final * direction_final * delta + velocity_addition global_position += velocity rotation = rotation_final scale = scale_final @@ -440,8 +479,17 @@ func process_behavior_context_request( pass return +func process_randomness() -> void: + if speed_random != Vector3.ZERO: + speed = ProjectileEngine.get_random_float_value(speed_random) + if texture_rotation_random != Vector3.ZERO: + texture_rotation = ProjectileEngine.get_random_float_value(texture_rotation_random) + + func queue_free_projectile() -> void: - projectile_node_manager.active_nodes.erase(self) + if projectile_node_manager: + if projectile_node_manager.active_nodes.has(self): + projectile_node_manager.active_nodes.erase(self) if projectile_node_index >= 0: active = false visible = false diff --git a/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_laser_2d/projectile_laser_2d.gd b/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_laser_2d/projectile_laser_2d.gd index b61bd486..d197d80a 100644 --- a/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_laser_2d/projectile_laser_2d.gd +++ b/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_laser_2d/projectile_laser_2d.gd @@ -64,7 +64,11 @@ enum LaserType { var laser_line_2d : Line2D +var _current_laser_length: float = 0 + func _ready() -> void: + apply_custom_data() + collision_width = laser_width + 2 super() pass @@ -73,16 +77,22 @@ func setup_projectile_2d() -> void: init_base_properties() setup_projectile_behavior() update_projectile_2d(get_physics_process_delta_time()) + setup_collision_raycast() setup_projectile_laser() pass func _physics_process(delta: float) -> void: super(delta) + + update_collision_raycast() + if start_full_length: laser_line_2d.clear_points() laser_line_2d.add_point(laser_line_2d.position) - laser_line_2d.add_point(laser_line_2d.position + direction.rotated(direction_rotation) * laser_length) - collision_shape.global_position = global_position + direction.rotated(direction_rotation) * laser_length / 2 + laser_line_2d.add_point(laser_line_2d.position + direction * _current_laser_length) + collision_shape.position = laser_line_2d.position + direction * _current_laser_length / 2 + collision_shape.shape.size = Vector2(_current_laser_length, collision_width) + collision_shape.rotation = direction.angle() else: laser_line_2d.clear_points() match laser_type: @@ -93,22 +103,24 @@ func _physics_process(delta: float) -> void: laser_line_2d.add_point(laser_line_2d.position) collision_shape.global_position = global_position - direction_final * life_distance / 2 collision_shape.shape.size = Vector2(life_distance, collision_width) - collision_shape.rotation = direction_rotation + # collision_shape.rotation = direction_rotation else: laser_line_2d.add_point(laser_line_2d.position - direction_final * laser_length) laser_line_2d.add_point(laser_line_2d.position) collision_shape.global_position = global_position - direction_final * laser_length / 2 collision_shape.shape.size = Vector2(laser_length, collision_width) - collision_shape.rotation = direction_rotation + # collision_shape.rotation = direction_rotation _: pass - # update_laser_line_2d() + # print("lazer: ", collision_shape.global_position) + pass func update_laser_line_2d() -> void: if !laser_line_2d: return + update_collision_raycast() laser_line_2d.texture = texture laser_line_2d.texture_mode = texture_mode laser_line_2d.width_curve = width_curve @@ -120,12 +132,13 @@ func update_laser_line_2d() -> void: match laser_type: LaserType.STRAIGHT: laser_line_2d.add_point(laser_line_2d.position) - laser_line_2d.add_point(laser_line_2d.position + direction_final * laser_length) + laser_line_2d.add_point(laser_line_2d.position + direction * _current_laser_length) _: pass - collision_shape.global_position = global_position + direction_final * laser_length / 2 - collision_shape.shape.size = Vector2(laser_length, collision_width) - collision_shape.rotation = direction_rotation + collision_shape.position = laser_line_2d.position + direction * _current_laser_length / 2 + collision_shape.shape.size = Vector2(_current_laser_length, collision_width) + collision_shape.rotation = direction.angle() + # collision_shape.rotation = direction_rotation pass @@ -133,13 +146,6 @@ func setup_projectile_laser() -> void: if laser_line_2d: laser_line_2d.queue_free() laser_line_2d = Line2D.new() - laser_line_2d.texture = texture - laser_line_2d.texture_mode = texture_mode - laser_line_2d.width_curve = width_curve - laser_line_2d.joint_mode = Line2D.LINE_JOINT_ROUND - laser_line_2d.begin_cap_mode = begin_cap_mode - laser_line_2d.end_cap_mode = end_cap_mode - laser_line_2d.width = laser_width add_child(laser_line_2d, true) laser_line_2d.clear_points() if !collision_shape: @@ -151,15 +157,32 @@ func setup_projectile_laser() -> void: var _rect_shape = RectangleShape2D.new() collision_shape.shape = _rect_shape if start_full_length: - laser_line_2d.add_point(laser_line_2d.position) - laser_line_2d.add_point(laser_line_2d.position + direction.rotated(direction_rotation) * laser_length) - collision_shape.shape.size = Vector2(laser_length, laser_width) * 0.8 - collision_shape.global_position = global_position + direction.rotated(direction_rotation) * laser_length / 2 + _current_laser_length = laser_length + pass else: - collision_shape.position = global_position + collision_shape.position = position collision_shape.shape.size = Vector2(1, collision_width) + update_laser_line_2d() + pass + + + +var collision_raycast: RayCast2D + +func setup_collision_raycast() -> void: + collision_raycast = RayCast2D.new() + add_child(collision_raycast) + collision_raycast.set_collision_mask_value(6, true) + pass + + +func update_collision_raycast() -> void: + collision_raycast.global_position = laser_line_2d.global_position + collision_raycast.target_position = direction * laser_length + collision_raycast.force_raycast_update() + if collision_raycast.is_colliding(): + _current_laser_length = collision_raycast.global_position.distance_to(collision_raycast.get_collision_point()) + else: + _current_laser_length = laser_length - collision_shape.rotation = direction_rotation - _: - pass pass diff --git a/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_node_manager.gd b/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_node_manager.gd index 35a3af52..ca0663d6 100644 --- a/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_node_manager.gd +++ b/addons/godot_projectile_engine/core/projectile_template/projectile_template_node_2d/projectile_node_manager.gd @@ -33,7 +33,7 @@ func setup_projectile_manager() -> void: print_debug(_instantiate_node, " is not Projectile2D") _is_valid_projectile_node_2d = false return - + _is_valid_projectile_node_2d = true create_projectile_pool() pass @@ -47,6 +47,7 @@ func create_projectile_pool() -> void: return for _index in projectile_max_pooling: _projectile_node_2d = projectile_node_2d_packedscene.instantiate() + _projectile_node_2d.projectile_template_2d = projectile_template_2d _projectile_node_2d.projectile_node_manager = self _projectile_node_2d.projectile_node_index = _index _projectile_node_2d.active = false @@ -67,6 +68,7 @@ func spawn_projectile_pattern(pattern_composer_pack: Array[PatternComposerData]) if projectile_max_pooling > 0: for _pattern_composer_data : PatternComposerData in pattern_composer_pack: _projectile_node_2d = projectile_node_array[projectile_pooling_index] + _projectile_node_2d.projectile_template_2d = projectile_template_2d _projectile_node_2d.active = true _projectile_node_2d.visible = true _projectile_node_2d.monitoring = true @@ -85,7 +87,9 @@ func spawn_projectile_pattern(pattern_composer_pack: Array[PatternComposerData]) pass else: for _pattern_composer_data : PatternComposerData in pattern_composer_pack: + _projectile_node_2d = null _projectile_node_2d = projectile_node_2d_packedscene.instantiate() + _projectile_node_2d.projectile_template_2d = projectile_template_2d _projectile_node_2d.projectile_node_manager = self _projectile_node_2d.projectile_node_index = -1 _projectile_node_2d.apply_pattern_composer_data(_pattern_composer_data) diff --git a/addons/godot_projectile_engine/core/projectile_template/projectile_template_simple_2d/projectile_updater_simple_2d.gd b/addons/godot_projectile_engine/core/projectile_template/projectile_template_simple_2d/projectile_updater_simple_2d.gd index edcf87c3..fe24e233 100644 --- a/addons/godot_projectile_engine/core/projectile_template/projectile_template_simple_2d/projectile_updater_simple_2d.gd +++ b/addons/godot_projectile_engine/core/projectile_template/projectile_template_simple_2d/projectile_updater_simple_2d.gd @@ -104,11 +104,11 @@ func update_projectile_instances(delta: float) -> void: projectile_life_time_second_max = projectile_template_2d.life_time_second_max projectile_life_distance_max = projectile_template_2d.life_distance_max projectile_texture_rotate_direction = projectile_template_2d.texture_rotate_direction + var _overlap_collision_layer : int # Check for projectile destroy condition for index: int in projectile_active_index: _projectile_instance = projectile_instance_array[index] - # Life Time & Distance if _projectile_instance.life_time_second_max >= 0: _projectile_instance.life_time_second += delta @@ -125,14 +125,22 @@ func update_projectile_instances(delta: float) -> void: if destroy_on_area_collide: if has_overlapping_areas(index): for _overlap_area in get_overlapping_areas(index): - if not _overlap_area.collision_layer & projectile_collision_mask: + # if !ProjectileEngine: return + _overlap_collision_layer = ProjectileEngine.get_collider_collision_layer(_overlap_area) + if not _overlap_collision_layer & projectile_collision_mask: continue projectile_remove_index.append(index) if destroy_on_body_collide: if has_overlapping_bodies(index): for _overlap_body in get_overlapping_bodies(index): - if not _overlap_body.collision_layer & projectile_collision_mask: + # print("ProjectileEngine ProjectileEngine: ", ProjectileEngine) + # print(_overlap_body) + if !_overlap_body: + get_overlapping_bodies(index).erase(_overlap_body) + continue + _overlap_collision_layer = ProjectileEngine.get_collider_collision_layer(_overlap_body) + if !_overlap_collision_layer & projectile_collision_mask: continue projectile_remove_index.append(index) diff --git a/addons/godot_projectile_engine/core/projectile_wrapper/projectile_wrapper_2d.gd b/addons/godot_projectile_engine/core/projectile_wrapper/projectile_wrapper_2d.gd index 4f060fc8..ceb3be68 100644 --- a/addons/godot_projectile_engine/core/projectile_wrapper/projectile_wrapper_2d.gd +++ b/addons/godot_projectile_engine/core/projectile_wrapper/projectile_wrapper_2d.gd @@ -2,14 +2,19 @@ extends Node2D class_name ProjectileWrapper2D +signal projectile_wrapper_finished + @export var active : bool = false: set(value): active = value if is_instance_valid(projectile_spawner_2d): projectile_spawner_2d.active = value + for _spawner in projectile_spawners: + if is_instance_valid(_spawner): + _spawner.active = value @export var projectile_wrapper_name : String @export var projectile_spawner_2d : ProjectileSpawner2D - +@export var projectile_spawners: Array[ProjectileSpawner2D] @@ -21,6 +26,9 @@ func _ready() -> void: _register_projectile_wrapper(projectile_wrapper_name) if active and is_instance_valid(projectile_spawner_2d): projectile_spawner_2d.active = active + if projectile_spawner_2d: + if projectile_spawner_2d.timing_scheduler: + connect_timing_scheduler() func _register_projectile_wrapper(_projectile_wrapper_name: String) -> void: if _projectile_wrapper_name == "": return @@ -39,4 +47,13 @@ func _deregister_projectile_wrapper(_projectile_wrapper_name: String) -> void: _projectile_wrapper_nodes.erase(self) if _projectile_wrapper_nodes.size() <= 0: ProjectileEngine.projectile_wrapper_2d_nodes.erase(_projectile_wrapper_name) - + + +func connect_timing_scheduler() -> void: + projectile_spawner_2d.timing_scheduler.scheduler_completed.connect(_on_timing_scheduler_completed) + pass + + +func _on_timing_scheduler_completed() -> void: + projectile_wrapper_finished.emit() + pass \ No newline at end of file diff --git a/addons/godot_projectile_engine/core/timing_scheduler/base/timing_scheduler_component.gd b/addons/godot_projectile_engine/core/timing_scheduler/base/timing_scheduler_component.gd index 0429d0eb..8b76cd00 100644 --- a/addons/godot_projectile_engine/core/timing_scheduler/base/timing_scheduler_component.gd +++ b/addons/godot_projectile_engine/core/timing_scheduler/base/timing_scheduler_component.gd @@ -10,14 +10,21 @@ enum UpdateMode { INHERIT, ## Inherit the [code]update_mode[/code] from the parent TimingScheduler node. } +enum TimingMode { + INSTANT, ## Timed instantly when process + RELEASE, ## Timed after timing finished + NO, ## No timed emited. +} + @export var active : bool = true: set(value): active = value if !value: clear_timing_timer() - +@export var timing_mode: TimingMode = TimingMode.INSTANT @export var update_mode: UpdateMode = UpdateMode.INHERIT + var timing_timer: Timer var request_stop : bool = false diff --git a/addons/godot_projectile_engine/core/timing_scheduler/cooldown/tsc_cooldown.gd b/addons/godot_projectile_engine/core/timing_scheduler/cooldown/tsc_cooldown.gd index 238ff3af..ebb8e73c 100644 --- a/addons/godot_projectile_engine/core/timing_scheduler/cooldown/tsc_cooldown.gd +++ b/addons/godot_projectile_engine/core/timing_scheduler/cooldown/tsc_cooldown.gd @@ -9,16 +9,19 @@ class_name TSCCooldown ## Starts the cooldown timer with the next timing value func start_next_timing_value() -> void: - tsc_timed.emit() + if timing_mode == TimingMode.INSTANT: + tsc_timed.emit() # If duration is 0 or negative, complete immediately if cooldown_duration <= 0.0: tsc_completed.emit() return - # Start the timer with the cooldown duration timing_timer.start(cooldown_duration) + ## Called when the cooldown timer completes func on_timing_timer_timeout() -> void: + if timing_mode == TimingMode.RELEASE: + tsc_timed.emit() tsc_completed.emit() diff --git a/addons/godot_projectile_engine/core/timing_scheduler/timing_scheduler.gd b/addons/godot_projectile_engine/core/timing_scheduler/timing_scheduler.gd index 1d4153d5..333be1f1 100644 --- a/addons/godot_projectile_engine/core/timing_scheduler/timing_scheduler.gd +++ b/addons/godot_projectile_engine/core/timing_scheduler/timing_scheduler.gd @@ -26,8 +26,10 @@ enum StopMethod { SOFT_STOP, ## Finish current timing scheduler component before stopping } -const _DEFAULT_SEQUENCE_INDEX : int = -1 +const _DEFAULT_SEQUENCE_INDEX: int = -1 + +@export var active: bool = false ## If true, scheduler starts automatically when added to scene tree @export var autostart: bool = false @@ -38,34 +40,37 @@ const _DEFAULT_SEQUENCE_INDEX : int = -1 @export var update_method: UpdateMethod = UpdateMethod.TIMER ## Stop method (HARD_STOP or SOFT_STOP) -@export var stop_method : StopMethod = StopMethod.SOFT_STOP +@export var stop_method: StopMethod = StopMethod.SOFT_STOP ## Array of TimingSchedulerComponent nodes in sequence -var tsc_sequence : Array[TimingSchedulerComponent] +var tsc_sequence: Array[TimingSchedulerComponent] ## Current index in the timing sequence -var tsc_sequence_index : int = _DEFAULT_SEQUENCE_INDEX +var tsc_sequence_index: int = _DEFAULT_SEQUENCE_INDEX ## Currently active timing component -var current_tsc : TimingSchedulerComponent +var current_tsc: TimingSchedulerComponent ## Pause state of the scheduler -var paused : bool = true +var paused: bool = true -var _is_queue_soft_stop : bool = false +var _is_queue_soft_stop: bool = false -var _is_just_started : bool = false +var _is_just_started: bool = false func _ready() -> void: if autostart: call_deferred("start_scheduler") - func _physics_process(delta: float) -> void: - if _is_just_started: - _start_timing_scheduler() - _is_just_started = false + if active: + if paused: + start_scheduler() + start_timing_scheduler() + else: + if !paused: + stop_scheduler() pass @@ -74,22 +79,19 @@ func start_scheduler() -> void: if !paused: return _build_tsc_sequence() - _is_just_started = true ## Stops the timing scheduler using configured stop method func stop_scheduler() -> void: match stop_method: StopMethod.HARD_STOP: - _hard_stop_timing_scheduler() + hard_stop_timing_scheduler() StopMethod.SOFT_STOP: - _soft_stop_timing_scheduler() + soft_stop_timing_scheduler() - -func _start_timing_scheduler() -> void: - if tsc_sequence.is_empty(): +func start_timing_scheduler() -> void: + if tsc_sequence.is_empty(): return - if start_next_tsc(): paused = false else: @@ -97,21 +99,29 @@ func _start_timing_scheduler() -> void: scheduler_completed.emit() -func _hard_stop_timing_scheduler() -> void: +func force_stop_timing_scheduler() -> void: + if current_tsc: + current_tsc.stop_tsc() + _disconnect_tsc_signals(current_tsc) + current_tsc = null + paused = true + tsc_sequence_index = _DEFAULT_SEQUENCE_INDEX + pass + +func hard_stop_timing_scheduler() -> void: if not current_tsc: return - current_tsc.stop_tsc() _disconnect_tsc_signals(current_tsc) current_tsc = null paused = true tsc_sequence_index = _DEFAULT_SEQUENCE_INDEX + scheduler_completed.emit() -func _soft_stop_timing_scheduler() -> void: +func soft_stop_timing_scheduler() -> void: if not current_tsc: return - _disconnect_tsc_signals(current_tsc) current_tsc.request_stop = true if !current_tsc.tsc_completed.is_connected(_on_soft_stop_tsc_completed): @@ -138,8 +148,7 @@ func start_next_tsc() -> bool: current_tsc.stop_tsc() _disconnect_tsc_signals(current_tsc) current_tsc = next_tsc - current_tsc.tsc_timed.connect(_on_tsc_timed) - current_tsc.tsc_completed.connect(_on_tsc_completed) + _connect_tsc_signals(current_tsc) current_tsc.start_tsc() return true @@ -169,11 +178,13 @@ func _on_tsc_timed() -> void: ## Handles completion events from current component func _on_tsc_completed() -> void: - if not start_next_tsc(): + if !start_next_tsc(): _disconnect_tsc_signals(current_tsc) current_tsc = null paused = true + active = false tsc_sequence_index = _DEFAULT_SEQUENCE_INDEX + scheduler_completed.emit() ## Handles completion during soft stop @@ -183,10 +194,20 @@ func _on_soft_stop_tsc_completed() -> void: current_tsc.tsc_completed.disconnect(_on_soft_stop_tsc_completed) current_tsc = null tsc_sequence_index = _DEFAULT_SEQUENCE_INDEX + scheduler_completed.emit() +func _connect_tsc_signals(tsc: TimingSchedulerComponent) -> void: + if !tsc: return + if !tsc.is_connected("tsc_timed", _on_tsc_timed): + tsc.tsc_timed.connect(_on_tsc_timed) + if !tsc.is_connected("tsc_completed", _on_tsc_completed): + tsc.tsc_completed.connect(_on_tsc_completed) + pass + ## Disconnects signals from a timing component func _disconnect_tsc_signals(tsc: TimingSchedulerComponent) -> void: + if !tsc: return if tsc.is_connected("tsc_timed", _on_tsc_timed): tsc.tsc_timed.disconnect(_on_tsc_timed) if tsc.is_connected("tsc_completed", _on_tsc_completed): diff --git a/experiments/experiment_projectile_template/experiment_pattern_composer_7.tscn b/experiments/experiment_projectile_template/experiment_pattern_composer_7.tscn index cd8b55d5..7b1f51b8 100644 --- a/experiments/experiment_projectile_template/experiment_pattern_composer_7.tscn +++ b/experiments/experiment_projectile_template/experiment_pattern_composer_7.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=23 format=3 uid="uid://cuq1uhamp76cc"] +[gd_scene load_steps=26 format=3 uid="uid://cuq1uhamp76cc"] [ext_resource type="Script" uid="uid://dn7rm61wq3t3s" path="res://addons/godot_projectile_engine/core/projectile_environment/projectile_environment.gd" id="1_iv4ua"] [ext_resource type="Script" uid="uid://cnqgcej10tinn" path="res://addons/godot_projectile_engine/core/projectile_spawner/projectile_spawner.gd" id="2_phdum"] @@ -12,13 +12,15 @@ [ext_resource type="Script" uid="uid://1xodvevoujyr" path="res://addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/base/projectile_behavior_speed.gd" id="10_0ueia"] [ext_resource type="Texture2D" uid="uid://b6j1n8v6vliwk" path="res://addons/godot_projectile_engine/examples/assets/projectile/projectile_star_1.png" id="11_thm6q"] [ext_resource type="Script" uid="uid://cmlvc7d6q6iec" path="res://addons/godot_projectile_engine/core/projectile_template/base/projectile_behaviors/base/projectile_behavior_trigger.gd" id="12_s7nam"] +[ext_resource type="Script" uid="uid://cs3yq53jlx0po" path="res://addons/godot_projectile_engine/core/projectile_spawner/spawn_marker/projectile_spawn_maker_2d.gd" id="13_704py"] [ext_resource type="Script" uid="uid://bc73oiea731jc" path="res://addons/godot_projectile_engine/core/pattern_composer/pattern_composer.gd" id="14_5ovmj"] [ext_resource type="Script" uid="uid://co24jiat0y46s" path="res://addons/godot_projectile_engine/core/pattern_composer/component/PCCSingle2D.gd" id="15_tumm6"] [ext_resource type="Script" uid="uid://1bygasnif26d" path="res://addons/godot_projectile_engine/core/pattern_composer/component/PCCGroup.gd" id="16_1g6ig"] [ext_resource type="Script" uid="uid://cm1gi15tdc45s" path="res://addons/godot_projectile_engine/core/pattern_composer/component/PCCPolygon2D.gd" id="16_phdum"] [ext_resource type="Script" uid="uid://b8nclaklcf4uu" path="res://addons/godot_projectile_engine/core/timing_scheduler/timing_scheduler.gd" id="16_sxtnh"] [ext_resource type="Script" uid="uid://brqy6ew0t2b35" path="res://addons/godot_projectile_engine/core/pattern_composer/component/PCCSpread2D.gd" id="17_1g6ig"] -[ext_resource type="Script" uid="uid://sjpvs4m6jk71" path="res://addons/godot_projectile_engine/core/timing_scheduler/repeater/tsc_repeater.gd" id="17_bv78k"] +[ext_resource type="Script" uid="uid://cwr176dncae0f" path="res://addons/godot_projectile_engine/core/timing_scheduler/timing_set/tsc_timing_set.gd" id="20_704py"] +[ext_resource type="Script" uid="uid://ssup2p0b2bqp" path="res://addons/godot_projectile_engine/core/timing_scheduler/timing_set/base/timing_set.gd" id="21_rpb08"] [sub_resource type="Gradient" id="Gradient_td4cq"] offsets = PackedFloat32Array(0) @@ -29,32 +31,15 @@ gradient = SubResource("Gradient_td4cq") [sub_resource type="Resource" id="Resource_m0abk"] script = ExtResource("9_gvipp") -damage = 1.0 -speed = 100.0 -projectile_pooling_amount = 500 texture = ExtResource("11_thm6q") -scale = Vector2(1, 1) -texture_rotation = 0.0 -skew = 0.0 -texture_visible = true -texture_z_index = 0 -texture_modulate = Color(1, 1, 1, 1) -collision_layer = 0 -collision_mask = 0 -speed_projectile_behaviors = Array[ExtResource("10_0ueia")]([]) -direction_projectile_behaviors = Array[ExtResource("5_704py")]([]) -rotation_projectile_behaviors = Array[ExtResource("7_vcjro")]([]) -scale_projectile_behaviors = Array[ExtResource("8_qgwhk")]([]) -destroy_projectile_behaviors = Array[ExtResource("4_1rc6a")]([]) -piercing_projectile_behaviors = Array[ExtResource("6_rpb08")]([]) -bouncing_projectile_behaviors = Array[ExtResource("3_1g6ig")]([]) -trigger_projectile_behaviors = Array[ExtResource("12_s7nam")]([]) -speed_random = Vector3(0, 0, 0) -texture_rotation_random = Vector3(0, 0, 0) -scale_random = Vector3(0, 0, 0) -custom_data = [] metadata/_custom_type_script = "uid://d3yyxyx6shhya" +[sub_resource type="Resource" id="Resource_vcjro"] +script = ExtResource("21_rpb08") +entries = Array[float]([0.5, 1.0]) +repeat_count = -1 +metadata/_custom_type_script = "uid://ssup2p0b2bqp" + [node name="ExperimentPatternComposer7" type="Node2D"] [node name="Sprite2D" type="Sprite2D" parent="."] @@ -73,8 +58,19 @@ script = ExtResource("2_phdum") projectile_composer_name = "pattern_1" projectile_template_2d = SubResource("Resource_m0abk") timing_scheduler = NodePath("../TimingScheduler") +use_spawn_markers = true metadata/_custom_type_script = "uid://cnqgcej10tinn" +[node name="ProjectileSpawnMarker2D" type="Marker2D" parent="ProjectileSpawner2D4"] +position = Vector2(-128, -64) +script = ExtResource("13_704py") +metadata/_custom_type_script = "uid://cs3yq53jlx0po" + +[node name="ProjectileSpawnMarker2D2" type="Marker2D" parent="ProjectileSpawner2D4"] +position = Vector2(256, 0) +script = ExtResource("13_704py") +metadata/_custom_type_script = "uid://cs3yq53jlx0po" + [node name="PatternComposer2D" type="Node" parent="."] script = ExtResource("14_5ovmj") composer_name = "pattern_1" @@ -99,14 +95,13 @@ metadata/_custom_type_script = "uid://brqy6ew0t2b35" [node name="PCCPolygon2D" type="Node" parent="PatternComposer2D/PCCGroup"] script = ExtResource("16_phdum") polygon_sides = 4 -spread_out = false metadata/_custom_type_script = "uid://cm1gi15tdc45s" [node name="TimingScheduler" type="Node" parent="."] script = ExtResource("16_sxtnh") metadata/_custom_type_script = "uid://b8nclaklcf4uu" -[node name="TSCRepeater" type="Node" parent="TimingScheduler"] -script = ExtResource("17_bv78k") -duration = 0.2 -metadata/_custom_type_script = "uid://sjpvs4m6jk71" +[node name="TSCTimingSet" type="Node" parent="TimingScheduler"] +script = ExtResource("20_704py") +timing_set = SubResource("Resource_vcjro") +metadata/_custom_type_script = "uid://cwr176dncae0f" diff --git a/project.godot b/project.godot index 9cf281d1..d3524cad 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="GodotProjectileEngine" -config/features=PackedStringArray("4.4") +config/features=PackedStringArray("4.5") run/max_fps=60 [autoload] @@ -24,7 +24,7 @@ gdscript/warnings/untyped_declaration=2 [editor_plugins] -enabled=PackedStringArray("res://addons/gdUnit4/plugin.cfg", "res://addons/godot_projectile_engine/plugin.cfg", "res://addons/remove_orphan_uid/plugin.cfg") +enabled=PackedStringArray("res://addons/godot_projectile_engine/plugin.cfg", "res://addons/remove_orphan_uid/plugin.cfg", "res://addons/gdUnit4/plugin.cfg") [gdunit4]