From 9f47e272fbd7435581b4fc0d65ff6df27ae4ec40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20G=C3=A1lffy?= Date: Fri, 7 Nov 2025 20:03:54 +0100 Subject: [PATCH] chore: Upgrade vest to 1.10.2 --- .../vest/_generated-mixins/1-8182042.gd | 20 +- .../vest/_generated-mixins/1-8182042.gd.uid | 2 +- .../vest/_generated-mixins/2-ffc90559.gd.uid | 2 +- .../vest/_generated-mixins/3-27f38ba0.gd.uid | 2 +- .../vest/_generated-mixins/4-795b469a.gd.uid | 2 +- .../vest/_generated-mixins/5-96ae432f.gd.uid | 2 +- .../vest/_generated-mixins/6-3c4a4dd7.gd.uid | 2 +- .../addons/vest/cli/vest-cli-runner.gd | 93 ++++++ .../addons/vest/cli/vest-cli-runner.gd.uid | 1 + .../addons/vest/cli/vest-cli-scene.tscn | 4 +- trimsock.gd/addons/vest/cli/vest-cli.gd | 82 +---- .../addons/vest/commands/run-test-command.gd | 20 +- .../vest/icons/benchmark-fail.svg.import | 5 +- .../vest/icons/benchmark-pass.svg.import | 5 +- .../addons/vest/icons/benchmark.svg.import | 5 +- .../addons/vest/icons/clear.svg.import | 5 +- trimsock.gd/addons/vest/icons/collapse.svg | 68 ++++ .../addons/vest/icons/collapse.svg.import | 38 +++ .../addons/vest/icons/debug.svg.import | 5 +- trimsock.gd/addons/vest/icons/expand.svg | 68 ++++ .../addons/vest/icons/expand.svg.import | 38 +++ trimsock.gd/addons/vest/icons/fail.svg.import | 5 +- trimsock.gd/addons/vest/icons/hidden.svg | 1 + .../addons/vest/icons/hidden.svg.import | 38 +++ .../addons/vest/icons/jump-to.svg.import | 5 +- .../addons/vest/icons/lightbulb.svg.import | 5 +- trimsock.gd/addons/vest/icons/pass.svg.import | 5 +- .../addons/vest/icons/refresh.svg.import | 5 +- .../addons/vest/icons/run-save.svg.import | 5 +- trimsock.gd/addons/vest/icons/run.svg.import | 5 +- trimsock.gd/addons/vest/icons/search.svg | 1 + .../addons/vest/icons/search.svg.import | 37 +++ trimsock.gd/addons/vest/icons/skip.svg.import | 5 +- trimsock.gd/addons/vest/icons/spinner.svg | 125 ++++++++ .../addons/vest/icons/spinner.svg.import | 38 +++ trimsock.gd/addons/vest/icons/todo.svg.import | 5 +- trimsock.gd/addons/vest/icons/vest-icons.gd | 5 + trimsock.gd/addons/vest/icons/visibility.svg | 1 + .../addons/vest/icons/visibility.svg.import | 38 +++ trimsock.gd/addons/vest/icons/void.svg.import | 5 +- .../addons/vest/mocks/vest-mock-defs.gd | 1 - .../addons/vest/mocks/vest-mock-generator.gd | 2 +- trimsock.gd/addons/vest/plugin.cfg | 2 +- .../addons/vest/runner/vest-base-runner.gd | 10 +- .../addons/vest/runner/vest-daemon-runner.gd | 28 +- .../addons/vest/runner/vest-local-runner.gd | 111 +++++-- trimsock.gd/addons/vest/tap-reporter.gd | 7 +- .../vest/test/mixins/gather-suite-mixin.gd | 20 +- .../addons/vest/test/vest-test-base.gd | 49 ++- trimsock.gd/addons/vest/test/vest-test.gd | 2 +- trimsock.gd/addons/vest/ui/results-panel.gd | 292 ++++++++++++++++++ .../addons/vest/ui/results-panel.gd.uid | 1 + trimsock.gd/addons/vest/ui/results-panel.tscn | 76 +++++ trimsock.gd/addons/vest/ui/vest-ui.gd | 236 +++++++------- trimsock.gd/addons/vest/ui/vest-ui.tscn | 159 ++++++++-- .../addons/vest/ui/visibility-popup.gd | 70 +++++ .../addons/vest/ui/visibility-popup.gd.uid | 1 + .../addons/vest/ui/visibility-popup.tscn | 31 ++ trimsock.gd/addons/vest/vest-defs.gd | 28 +- trimsock.gd/addons/vest/vest-internals.gd | 5 + trimsock.gd/addons/vest/vest-result.gd | 33 +- trimsock.gd/addons/vest/vest-singleton.gd | 28 +- .../tests/incremental_id_generator.perf.gd | 2 +- trimsock.gd/tests/random_id_generator.perf.gd | 2 +- 64 files changed, 1645 insertions(+), 354 deletions(-) create mode 100644 trimsock.gd/addons/vest/cli/vest-cli-runner.gd create mode 100644 trimsock.gd/addons/vest/cli/vest-cli-runner.gd.uid create mode 100644 trimsock.gd/addons/vest/icons/collapse.svg create mode 100644 trimsock.gd/addons/vest/icons/collapse.svg.import create mode 100644 trimsock.gd/addons/vest/icons/expand.svg create mode 100644 trimsock.gd/addons/vest/icons/expand.svg.import create mode 100644 trimsock.gd/addons/vest/icons/hidden.svg create mode 100644 trimsock.gd/addons/vest/icons/hidden.svg.import create mode 100644 trimsock.gd/addons/vest/icons/search.svg create mode 100644 trimsock.gd/addons/vest/icons/search.svg.import create mode 100644 trimsock.gd/addons/vest/icons/spinner.svg create mode 100644 trimsock.gd/addons/vest/icons/spinner.svg.import create mode 100644 trimsock.gd/addons/vest/icons/visibility.svg create mode 100644 trimsock.gd/addons/vest/icons/visibility.svg.import create mode 100644 trimsock.gd/addons/vest/ui/results-panel.gd create mode 100644 trimsock.gd/addons/vest/ui/results-panel.gd.uid create mode 100644 trimsock.gd/addons/vest/ui/results-panel.tscn create mode 100644 trimsock.gd/addons/vest/ui/visibility-popup.gd create mode 100644 trimsock.gd/addons/vest/ui/visibility-popup.gd.uid create mode 100644 trimsock.gd/addons/vest/ui/visibility-popup.tscn diff --git a/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd b/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd index 4759f81..7f4e80b 100644 --- a/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd +++ b/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd @@ -61,9 +61,18 @@ func _get_suite() -> VestDefs.Suite: await call(method["name"]) for method in case_methods: - test(method["name"].trim_prefix("test").capitalize(), func(): await call(method["name"])) + var method_name := method["name"] as String + var test_name := method_name.trim_prefix("test").trim_suffix("__only").capitalize() + var callback := func(): await call(method["name"]) + var is_only := _is_only(method_name) + + test(test_name, callback, is_only, method_name) for method in parametric_methods: + var method_name := method["name"] as String + var test_name := method_name.trim_prefix("test").trim_suffix("__only").capitalize() + var is_only := _is_only(method_name) + var param_provider_name := method["default_args"][0] as String if not has_method(param_provider_name): push_warning( @@ -80,7 +89,12 @@ func _get_suite() -> VestDefs.Suite: for i in range(params.size()): test( - "%s#%d %s" % [method["name"].trim_prefix("test").capitalize(), i+1, params[i]], - func(): await callv(method["name"], params[i]) + "%s#%d %s" % [test_name, i+1, params[i]], + func(): await callv(method["name"], params[i]), + is_only, + method_name ) ) + +func _is_only(name: String) -> bool: + return name.ends_with("__only") diff --git a/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd.uid b/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd.uid index 727a363..b300e57 100644 --- a/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd.uid +++ b/trimsock.gd/addons/vest/_generated-mixins/1-8182042.gd.uid @@ -1 +1 @@ -uid://cg53q8t5aibti +uid://mm20mdmsc5ii diff --git a/trimsock.gd/addons/vest/_generated-mixins/2-ffc90559.gd.uid b/trimsock.gd/addons/vest/_generated-mixins/2-ffc90559.gd.uid index bba802c..97d7766 100644 --- a/trimsock.gd/addons/vest/_generated-mixins/2-ffc90559.gd.uid +++ b/trimsock.gd/addons/vest/_generated-mixins/2-ffc90559.gd.uid @@ -1 +1 @@ -uid://d168ndvsqupun +uid://caf7owv25i7op diff --git a/trimsock.gd/addons/vest/_generated-mixins/3-27f38ba0.gd.uid b/trimsock.gd/addons/vest/_generated-mixins/3-27f38ba0.gd.uid index ba129d9..c099974 100644 --- a/trimsock.gd/addons/vest/_generated-mixins/3-27f38ba0.gd.uid +++ b/trimsock.gd/addons/vest/_generated-mixins/3-27f38ba0.gd.uid @@ -1 +1 @@ -uid://8ojcq7eq3de3 +uid://d3mmot6r5ycm6 diff --git a/trimsock.gd/addons/vest/_generated-mixins/4-795b469a.gd.uid b/trimsock.gd/addons/vest/_generated-mixins/4-795b469a.gd.uid index fc69f9a..9021b7a 100644 --- a/trimsock.gd/addons/vest/_generated-mixins/4-795b469a.gd.uid +++ b/trimsock.gd/addons/vest/_generated-mixins/4-795b469a.gd.uid @@ -1 +1 @@ -uid://hlpirirrosh8 +uid://v3npn3goh4em diff --git a/trimsock.gd/addons/vest/_generated-mixins/5-96ae432f.gd.uid b/trimsock.gd/addons/vest/_generated-mixins/5-96ae432f.gd.uid index 14678fe..7dcf75d 100644 --- a/trimsock.gd/addons/vest/_generated-mixins/5-96ae432f.gd.uid +++ b/trimsock.gd/addons/vest/_generated-mixins/5-96ae432f.gd.uid @@ -1 +1 @@ -uid://hgh0houeje1w +uid://c8oa5ooaqwi11 diff --git a/trimsock.gd/addons/vest/_generated-mixins/6-3c4a4dd7.gd.uid b/trimsock.gd/addons/vest/_generated-mixins/6-3c4a4dd7.gd.uid index cb88ed1..a3ca195 100644 --- a/trimsock.gd/addons/vest/_generated-mixins/6-3c4a4dd7.gd.uid +++ b/trimsock.gd/addons/vest/_generated-mixins/6-3c4a4dd7.gd.uid @@ -1 +1 @@ -uid://bki88xn3pwurs +uid://dqqt1ecooeqdd diff --git a/trimsock.gd/addons/vest/cli/vest-cli-runner.gd b/trimsock.gd/addons/vest/cli/vest-cli-runner.gd new file mode 100644 index 0000000..c15fdb1 --- /dev/null +++ b/trimsock.gd/addons/vest/cli/vest-cli-runner.gd @@ -0,0 +1,93 @@ +extends RefCounted +class_name VestCLIRunner + +## Implements functionality to run tests + +var _peer: StreamPeerTCP = null + +## Run tests with [Params] +func run(params: VestCLI.Params) -> int: + var validation_errors := params.validate() + if not validation_errors.is_empty(): + for error in validation_errors: + OS.alert(error) + push_error(error) + return 1 + + await _connect(params) + + var results := await _run_tests(params) + _report(params, results) + _send_results_over_network(params, results) + + _disconnect() + + if results.get_aggregate_status() == VestResult.TEST_PASS: + print_rich("All tests [color=green]passed[/color]!") + return 0 + else: + print_rich("There are test [color=red]failures[/color]!") + return 1 + +func _run_tests(params: VestCLI.Params) -> VestResult.Suite: + var runner := VestLocalRunner.new() + runner.on_partial_result.connect(func(result: VestResult.Suite): + if _peer != null: + if result != null: + _peer.put_var(result._to_wire(), true) + else: + _peer.put_var(result, true) + ) + + var results: VestResult.Suite + if params.run_file: + results = await runner.run_script_at(params.run_file, params.only_mode) + elif params.run_glob: + results = await runner.run_glob(params.run_glob, params.only_mode) + + return results + +func _report(params: VestCLI.Params, results: VestResult.Suite): + var report := TAPReporter.report(results) + + if params.report_format: + if params.report_file in ["", "-"]: + print(report) + else: + var fa := FileAccess.open(params.report_file, FileAccess.WRITE) + fa.store_string(report) + fa.close() + +func _connect(params: VestCLI.Params): + if not params.host and params.port == -1: + return + + var host := params.host + var port := params.port + + if not host: host = "0.0.0.0" + if port == -1: port = 54932 + + var peer := StreamPeerTCP.new() + var err := peer.connect_to_host(host, port) + if err != OK: + push_warning("Couldn't connect on port %d! %s" % [port, error_string(err)]) + return + + await Vest.until(func(): peer.poll(); return peer.get_status() != StreamPeerTCP.STATUS_CONNECTING) + if peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: + push_warning("Connection failed! Socket status: %d" % [peer.get_status()]) + return + + peer.set_no_delay(true) + _peer = peer + +func _disconnect(): + if _peer != null: + _peer.disconnect_from_host() + +func _send_results_over_network(params: VestCLI.Params, results: VestResult.Suite): + if not _peer: + return + + _peer.put_var(results._to_wire(), true) diff --git a/trimsock.gd/addons/vest/cli/vest-cli-runner.gd.uid b/trimsock.gd/addons/vest/cli/vest-cli-runner.gd.uid new file mode 100644 index 0000000..bad75d7 --- /dev/null +++ b/trimsock.gd/addons/vest/cli/vest-cli-runner.gd.uid @@ -0,0 +1 @@ +uid://bw0ajf5t50wi0 diff --git a/trimsock.gd/addons/vest/cli/vest-cli-scene.tscn b/trimsock.gd/addons/vest/cli/vest-cli-scene.tscn index 49e1f79..6bb7a49 100644 --- a/trimsock.gd/addons/vest/cli/vest-cli-scene.tscn +++ b/trimsock.gd/addons/vest/cli/vest-cli-scene.tscn @@ -3,13 +3,15 @@ [sub_resource type="GDScript" id="GDScript_de1pc"] script/source = "extends Node +# Used to run tests in debug mode + func _ready(): Vest._register_scene_tree(get_tree()) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) var params := Vest.__.LocalSettings.run_params - var runner := VestCLI.Runner.new() + var runner := VestCLIRunner.new() var exit_code := await runner.run(params) get_tree().quit(exit_code) diff --git a/trimsock.gd/addons/vest/cli/vest-cli.gd b/trimsock.gd/addons/vest/cli/vest-cli.gd index 98c55d5..6b4ac5b 100644 --- a/trimsock.gd/addons/vest/cli/vest-cli.gd +++ b/trimsock.gd/addons/vest/cli/vest-cli.gd @@ -19,6 +19,9 @@ class Params: ## Path for saving the report var report_file: String = "" + ## How to handle tests marked as `only` + var only_mode: int = Vest.__.ONLY_DISABLED + ## Host to connect to for sending results var host: String = "" @@ -50,6 +53,11 @@ class Params: if host: result.append_array(["--vest-host", host]) if port != -1: result.append_array(["--vest-port", str(port)]) + match only_mode: + Vest.__.ONLY_DISABLED: result.append("--no-only") + Vest.__.ONLY_AUTO: result.append("--auto-only") + Vest.__.ONLY_ENABLED: result.append("--only") + return result ## Parse an array of CLI parameters. @@ -68,78 +76,12 @@ class Params: elif arg == "--vest-report-format": result.report_format = val elif arg == "--vest-port": result.port = val.to_int() elif arg == "--vest-host": result.host = val + elif arg == "--no-only": result.only_mode = Vest.__.ONLY_DISABLED + elif arg == "--only": result.only_mode = Vest.__.ONLY_ENABLED + elif arg == "--auto-only": result.only_mode = Vest.__.ONLY_AUTO return result -## Implements functionality to run tests -class Runner: - ## Run tests with [Params] - func run(params: Params) -> int: - var validation_errors := params.validate() - if not validation_errors.is_empty(): - for error in validation_errors: - OS.alert(error) - push_error(error) - return 1 - - var results := await _run_tests(params) - _report(params, results) - await _send_results_over_network(params, results) - - if results.get_aggregate_status() == VestResult.TEST_PASS: - print_rich("All tests [color=green]passed[/color]!") - return 0 - else: - print_rich("There are test [color=red]failures[/color]!") - return 1 - - func _run_tests(params: Params) -> VestResult.Suite: - var runner := VestLocalRunner.new() - - var results: VestResult.Suite - if params.run_file: - results = await runner.run_script_at(params.run_file) - elif params.run_glob: - results = await runner.run_glob(params.run_glob) - - return results - - func _report(params: Params, results: VestResult.Suite): - var report := TAPReporter.report(results) - - if params.report_format: - if params.report_file in ["", "-"]: - print(report) - else: - var fa := FileAccess.open(params.report_file, FileAccess.WRITE) - fa.store_string(report) - fa.close() - - func _send_results_over_network(params: Params, results: VestResult.Suite): - if not params.host and params.port == -1: - return - - var host := params.host - var port := params.port - - if not host: host = "0.0.0.0" - if port == -1: port = 54932 - - var peer := StreamPeerTCP.new() - var err := peer.connect_to_host(host, port) - if err != OK: - push_warning("Couldn't connect on port %d! %s" % [port, error_string(err)]) - return - - await Vest.until(func(): peer.poll(); return peer.get_status() != StreamPeerTCP.STATUS_CONNECTING) - if peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: - push_warning("Connection failed! Socket status: %d" % [peer.get_status()]) - return - - peer.put_var(results._to_wire(), true) - peer.disconnect_from_host() - - ## Run vest CLI with parameters. ## [br][br] ## Returns the spawned process' ID. @@ -161,7 +103,7 @@ func _init(): await process_frame var params := Params.parse(OS.get_cmdline_args()) - var runner := Runner.new() + var runner := VestCLIRunner.new() var exit_code := await runner.run(params) diff --git a/trimsock.gd/addons/vest/commands/run-test-command.gd b/trimsock.gd/addons/vest/commands/run-test-command.gd index 4389224..13ed67d 100644 --- a/trimsock.gd/addons/vest/commands/run-test-command.gd +++ b/trimsock.gd/addons/vest/commands/run-test-command.gd @@ -1,24 +1,13 @@ @tool extends Node -static var _instance = null - -static func find(): - return _instance - -static func execute() -> void: - if _instance: - _instance.create_test() - else: - push_warning("No instance of Create Test command found!") - func run_test(): - _run(false) + _run(false, Vest.__.ONLY_AUTO) func debug_test(): - _run(true) + _run(true, Vest.__.ONLY_AUTO) -func _run(is_debug: bool) -> void: +func _run(is_debug: bool, only_mode: int) -> void: var editor_interface := Vest._get_editor_interface() var script_editor := editor_interface.get_script_editor() as ScriptEditor @@ -34,7 +23,7 @@ func _run(is_debug: bool) -> void: print_verbose("Running test \"%s\"" % [edited_script.resource_path]) var vest_ui := VestUI._get_ui() - vest_ui.run_script(edited_script, is_debug) + vest_ui.run_script(edited_script, is_debug, only_mode) func _is_ancestor_of(base_script: Script, script: Script) -> bool: for i in range(128): # Prevent runaway loops @@ -45,7 +34,6 @@ func _is_ancestor_of(base_script: Script, script: Script) -> bool: return false func _ready(): - _instance = self var editor := Vest._get_editor_interface() editor.get_command_palette().add_command("Run test", "vest/run-test", run_test, "F7") editor.get_command_palette().add_command("Debug test", "vest/debug-test", debug_test, "Ctrl+F7") diff --git a/trimsock.gd/addons/vest/icons/benchmark-fail.svg.import b/trimsock.gd/addons/vest/icons/benchmark-fail.svg.import index 122eb8c..5056f26 100644 --- a/trimsock.gd/addons/vest/icons/benchmark-fail.svg.import +++ b/trimsock.gd/addons/vest/icons/benchmark-fail.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://c1cdr1khuiy2f" path="res://.godot/imported/benchmark-fail.svg-b20a3c22c391ea7bcebd7f59f83ca9d5.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/benchmark-pass.svg.import b/trimsock.gd/addons/vest/icons/benchmark-pass.svg.import index d790d50..9ca506f 100644 --- a/trimsock.gd/addons/vest/icons/benchmark-pass.svg.import +++ b/trimsock.gd/addons/vest/icons/benchmark-pass.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://bt8iem8l0pq4s" path="res://.godot/imported/benchmark-pass.svg-7479ef55a95006e5fb1a614e003877eb.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/benchmark.svg.import b/trimsock.gd/addons/vest/icons/benchmark.svg.import index 19e8983..7b9229d 100644 --- a/trimsock.gd/addons/vest/icons/benchmark.svg.import +++ b/trimsock.gd/addons/vest/icons/benchmark.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://b6yh8aylojfgt" path="res://.godot/imported/benchmark.svg-b9f24369bdd89bc588a152cef6f09c00.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/clear.svg.import b/trimsock.gd/addons/vest/icons/clear.svg.import index b7fc175..195375a 100644 --- a/trimsock.gd/addons/vest/icons/clear.svg.import +++ b/trimsock.gd/addons/vest/icons/clear.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://ctdipwc8sklwo" path="res://.godot/imported/clear.svg-c74aed6190d9cab6b3b5d7c8ad724085.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/collapse.svg b/trimsock.gd/addons/vest/icons/collapse.svg new file mode 100644 index 0000000..b92a25a --- /dev/null +++ b/trimsock.gd/addons/vest/icons/collapse.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/trimsock.gd/addons/vest/icons/collapse.svg.import b/trimsock.gd/addons/vest/icons/collapse.svg.import new file mode 100644 index 0000000..83d5e88 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/collapse.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cv8va2smpv1lf" +path="res://.godot/imported/collapse.svg-9b497d9ee40a464a0f26821fb8538f0b.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/vest/icons/collapse.svg" +dest_files=["res://.godot/imported/collapse.svg-9b497d9ee40a464a0f26821fb8538f0b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/debug.svg.import b/trimsock.gd/addons/vest/icons/debug.svg.import index 007a7ed..2a33651 100644 --- a/trimsock.gd/addons/vest/icons/debug.svg.import +++ b/trimsock.gd/addons/vest/icons/debug.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://dwhlyf5eyiect" path="res://.godot/imported/debug.svg-10ec6677d3bd3a64aea2006222e97a3c.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/expand.svg b/trimsock.gd/addons/vest/icons/expand.svg new file mode 100644 index 0000000..fdb09d7 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/expand.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/trimsock.gd/addons/vest/icons/expand.svg.import b/trimsock.gd/addons/vest/icons/expand.svg.import new file mode 100644 index 0000000..34df78c --- /dev/null +++ b/trimsock.gd/addons/vest/icons/expand.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://sx0233b14cll" +path="res://.godot/imported/expand.svg-6ad1f463c41f73d2aadcc7559e7670ff.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/vest/icons/expand.svg" +dest_files=["res://.godot/imported/expand.svg-6ad1f463c41f73d2aadcc7559e7670ff.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/fail.svg.import b/trimsock.gd/addons/vest/icons/fail.svg.import index 94520aa..fd55afe 100644 --- a/trimsock.gd/addons/vest/icons/fail.svg.import +++ b/trimsock.gd/addons/vest/icons/fail.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://de5ileacgwme1" path="res://.godot/imported/fail.svg-fb1453c71ffec14739e9280d33cb8120.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/hidden.svg b/trimsock.gd/addons/vest/icons/hidden.svg new file mode 100644 index 0000000..8abbe79 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/hidden.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/trimsock.gd/addons/vest/icons/hidden.svg.import b/trimsock.gd/addons/vest/icons/hidden.svg.import new file mode 100644 index 0000000..b87b8be --- /dev/null +++ b/trimsock.gd/addons/vest/icons/hidden.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://buuq6qptg3vll" +path="res://.godot/imported/hidden.svg-58af1b7fb82759e99e9f427f659b9a32.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/vest/icons/hidden.svg" +dest_files=["res://.godot/imported/hidden.svg-58af1b7fb82759e99e9f427f659b9a32.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/jump-to.svg.import b/trimsock.gd/addons/vest/icons/jump-to.svg.import index eba8300..049431c 100644 --- a/trimsock.gd/addons/vest/icons/jump-to.svg.import +++ b/trimsock.gd/addons/vest/icons/jump-to.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://beoxvhp204wuv" path="res://.godot/imported/jump-to.svg-c9add894b2d5164c6f4e6837154c4f30.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/lightbulb.svg.import b/trimsock.gd/addons/vest/icons/lightbulb.svg.import index 83b1998..707e301 100644 --- a/trimsock.gd/addons/vest/icons/lightbulb.svg.import +++ b/trimsock.gd/addons/vest/icons/lightbulb.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://ckxkehgmjtjcx" path="res://.godot/imported/lightbulb.svg-f4f05ab673622fe0cb66314541b2eac4.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/pass.svg.import b/trimsock.gd/addons/vest/icons/pass.svg.import index 03fc7b3..6acccf3 100644 --- a/trimsock.gd/addons/vest/icons/pass.svg.import +++ b/trimsock.gd/addons/vest/icons/pass.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://bmqjme87oq3xx" path="res://.godot/imported/pass.svg-527f701477d1793b7212aa92a3d53194.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/refresh.svg.import b/trimsock.gd/addons/vest/icons/refresh.svg.import index 4e8b6b3..bf5bf4e 100644 --- a/trimsock.gd/addons/vest/icons/refresh.svg.import +++ b/trimsock.gd/addons/vest/icons/refresh.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://dt75n55vr4kq0" path="res://.godot/imported/refresh.svg-08b8d1fd09107d0faa0c33acbadb6352.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/run-save.svg.import b/trimsock.gd/addons/vest/icons/run-save.svg.import index ca548dc..4d55d54 100644 --- a/trimsock.gd/addons/vest/icons/run-save.svg.import +++ b/trimsock.gd/addons/vest/icons/run-save.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://c711af0y1s2ct" path="res://.godot/imported/run-save.svg-284dc56c3fcbb3decae07a3263265580.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/run.svg.import b/trimsock.gd/addons/vest/icons/run.svg.import index 93ad9ff..ec7b6d6 100644 --- a/trimsock.gd/addons/vest/icons/run.svg.import +++ b/trimsock.gd/addons/vest/icons/run.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://r4y6ihamgino" path="res://.godot/imported/run.svg-b6532e07a4db7efc49be28be04360e08.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/search.svg b/trimsock.gd/addons/vest/icons/search.svg new file mode 100644 index 0000000..110a512 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/trimsock.gd/addons/vest/icons/search.svg.import b/trimsock.gd/addons/vest/icons/search.svg.import new file mode 100644 index 0000000..99d1c51 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/search.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cl6im4a1uj6nq" +path="res://.godot/imported/search.svg-f595f7d0e550dfff682417fb006673bc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/vest/icons/search.svg" +dest_files=["res://.godot/imported/search.svg-f595f7d0e550dfff682417fb006673bc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/trimsock.gd/addons/vest/icons/skip.svg.import b/trimsock.gd/addons/vest/icons/skip.svg.import index 1c928fd..972820e 100644 --- a/trimsock.gd/addons/vest/icons/skip.svg.import +++ b/trimsock.gd/addons/vest/icons/skip.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://cx4htkmtgdk5l" path="res://.godot/imported/skip.svg-05ed8ec36ac4803550bae54821df0a30.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/spinner.svg b/trimsock.gd/addons/vest/icons/spinner.svg new file mode 100644 index 0000000..f6f3d39 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/spinner.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/trimsock.gd/addons/vest/icons/spinner.svg.import b/trimsock.gd/addons/vest/icons/spinner.svg.import new file mode 100644 index 0000000..a0cdc23 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/spinner.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://csfyamlc15vp0" +path="res://.godot/imported/spinner.svg-2fba1cd10beb909105907ca61911cf40.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/vest/icons/spinner.svg" +dest_files=["res://.godot/imported/spinner.svg-2fba1cd10beb909105907ca61911cf40.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/todo.svg.import b/trimsock.gd/addons/vest/icons/todo.svg.import index 1d3aaae..38cd422 100644 --- a/trimsock.gd/addons/vest/icons/todo.svg.import +++ b/trimsock.gd/addons/vest/icons/todo.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://meh1sny670to" path="res://.godot/imported/todo.svg-2340992753f3f9d2c971234e5e126d95.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/vest-icons.gd b/trimsock.gd/addons/vest/icons/vest-icons.gd index fb4058e..a2ec9c8 100644 --- a/trimsock.gd/addons/vest/icons/vest-icons.gd +++ b/trimsock.gd/addons/vest/icons/vest-icons.gd @@ -20,3 +20,8 @@ const clear: Texture2D = preload("res://addons/vest/icons/clear.svg") const jump_to: Texture2D = preload("res://addons/vest/icons/jump-to.svg") const refresh: Texture2D = preload("res://addons/vest/icons/refresh.svg") const lightbulb: Texture2D = preload("res://addons/vest/icons/lightbulb.svg") + +const visible: Texture2D = preload("res://addons/vest/icons/visibility.svg") +const hidden: Texture2D = preload("res://addons/vest/icons/hidden.svg") +const expand: Texture2D = preload("res://addons/vest/icons/expand.svg") +const collapse: Texture2D = preload("res://addons/vest/icons/collapse.svg") diff --git a/trimsock.gd/addons/vest/icons/visibility.svg b/trimsock.gd/addons/vest/icons/visibility.svg new file mode 100644 index 0000000..768d6ee --- /dev/null +++ b/trimsock.gd/addons/vest/icons/visibility.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/trimsock.gd/addons/vest/icons/visibility.svg.import b/trimsock.gd/addons/vest/icons/visibility.svg.import new file mode 100644 index 0000000..5977a86 --- /dev/null +++ b/trimsock.gd/addons/vest/icons/visibility.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bpyua1ic4p47h" +path="res://.godot/imported/visibility.svg-2226fcfdff15944d56e68e7df764b325.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/vest/icons/visibility.svg" +dest_files=["res://.godot/imported/visibility.svg-2226fcfdff15944d56e68e7df764b325.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/icons/void.svg.import b/trimsock.gd/addons/vest/icons/void.svg.import index a0c3658..59c1efb 100644 --- a/trimsock.gd/addons/vest/icons/void.svg.import +++ b/trimsock.gd/addons/vest/icons/void.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://ci8hqm6meom3h" path="res://.godot/imported/void.svg-dedfa2bc2d4e3cbf7106ebb34fd16279.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/trimsock.gd/addons/vest/mocks/vest-mock-defs.gd b/trimsock.gd/addons/vest/mocks/vest-mock-defs.gd index 791b6bb..c6d921a 100644 --- a/trimsock.gd/addons/vest/mocks/vest-mock-defs.gd +++ b/trimsock.gd/addons/vest/mocks/vest-mock-defs.gd @@ -35,7 +35,6 @@ class Answer: if args.size() != expected_args.size(): return false if args == expected_args: - print("Arg match!") return true # Do a lenient check, so users don't trip on unexpected diffs, like diff --git a/trimsock.gd/addons/vest/mocks/vest-mock-generator.gd b/trimsock.gd/addons/vest/mocks/vest-mock-generator.gd index 11caa81..2f0a133 100644 --- a/trimsock.gd/addons/vest/mocks/vest-mock-generator.gd +++ b/trimsock.gd/addons/vest/mocks/vest-mock-generator.gd @@ -41,7 +41,7 @@ func generate_mock_source(script: Script) -> String: mock_source.append( ("func %s(%s):\n" + - "\treturn __vest_mock_handler._handle(%s, [%s])\n\n") % + "\treturn __vest_mock_handler._handle(self.%s, [%s])\n\n") % [method_name, arg_def_string, method_name, arg_def_string] ) diff --git a/trimsock.gd/addons/vest/plugin.cfg b/trimsock.gd/addons/vest/plugin.cfg index 493868c..fae928a 100644 --- a/trimsock.gd/addons/vest/plugin.cfg +++ b/trimsock.gd/addons/vest/plugin.cfg @@ -3,5 +3,5 @@ name="vest" description="A unit testing library for Godot" author="Tamás Gálffy" -version="1.3.3" +version="1.10.2" script="plugin.gd" diff --git a/trimsock.gd/addons/vest/runner/vest-base-runner.gd b/trimsock.gd/addons/vest/runner/vest-base-runner.gd index e9a7b7e..9bea773 100644 --- a/trimsock.gd/addons/vest/runner/vest-base-runner.gd +++ b/trimsock.gd/addons/vest/runner/vest-base-runner.gd @@ -1,19 +1,19 @@ extends RefCounted -# TODO: Eventually support re-running single tests +signal on_partial_result(result: VestResult.Suite) -func run_script(_script: Script) -> VestResult.Suite: +func run_script(_script: Script, _only_mode: int = Vest.__.ONLY_DEFAULT) -> VestResult.Suite: # OVERRIDE return VestResult.Suite.new() -func run_glob(_p_glob: String) -> VestResult.Suite: +func run_glob(_p_glob: String, _only_mode: int = Vest.__.ONLY_DEFAULT) -> VestResult.Suite: # OVERRIDE return VestResult.Suite.new() -func run_script_at(path: String) -> VestResult.Suite: +func run_script_at(path: String, only_mode: int = Vest.__.ONLY_DEFAULT) -> VestResult.Suite: var test_script := load(path) if not test_script or not test_script is Script: return null - return await run_script(test_script) + return await run_script(test_script, only_mode) diff --git a/trimsock.gd/addons/vest/runner/vest-daemon-runner.gd b/trimsock.gd/addons/vest/runner/vest-daemon-runner.gd index a3a2964..2f86160 100644 --- a/trimsock.gd/addons/vest/runner/vest-daemon-runner.gd +++ b/trimsock.gd/addons/vest/runner/vest-daemon-runner.gd @@ -15,18 +15,20 @@ func with_debug() -> VestDaemonRunner: return self ## Run a test script -func run_script(script: Script) -> VestResult.Suite: +func run_script(script: Script, only_mode: int = Vest.__.ONLY_DEFAULT) -> VestResult.Suite: var params := VestCLI.Params.new() params.run_file = script.resource_path + params.only_mode = only_mode return await _run_with_params(params) ## Run test scripts matching glob ## [br][br] ## See [method String.match] -func run_glob(glob: String) -> VestResult.Suite: +func run_glob(glob: String, only_mode: int = Vest.__.ONLY_DEFAULT) -> VestResult.Suite: var params := VestCLI.Params.new() params.run_glob = glob + params.only_mode = only_mode return await _run_with_params(params) @@ -53,14 +55,24 @@ func _run_with_params(params: VestCLI.Params) -> VestResult.Suite: return null _peer = _server.take_connection() + var results = null - # Take results - if await timeout.until(func(): return _peer.get_available_bytes() > 0) != OK: - push_error("Didn't receive results in time! Available bytes: %d" % [_peer.get_available_bytes()]) - _stop() - return null + while true: + await Vest.sleep() + + _peer.poll() + if _peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: + break + + if _peer.get_available_bytes() <= 0: + # No data, wait some more + continue + + var message = _peer.get_var(true) + if message is Dictionary: + results = message + on_partial_result.emit(VestResult.Suite._from_wire(results)) - var results = _peer.get_var(true) _stop() if results == null: diff --git a/trimsock.gd/addons/vest/runner/vest-local-runner.gd b/trimsock.gd/addons/vest/runner/vest-local-runner.gd index 40833e7..c827a44 100644 --- a/trimsock.gd/addons/vest/runner/vest-local-runner.gd +++ b/trimsock.gd/addons/vest/runner/vest-local-runner.gd @@ -1,58 +1,121 @@ extends "res://addons/vest/runner/vest-base-runner.gd" class_name VestLocalRunner +var _result_buffer: VestResult.Suite + ## Run a test script -func run_script(script: Script) -> VestResult.Suite: +func run_script(script: Script, only_mode: int = Vest.__.ONLY_DEFAULT) -> VestResult.Suite: + var _result_buffer = VestResult.Suite.new() if not script: return null var test_instance = script.new() + if not test_instance is VestTest: + test_instance.free() + return null + var test := test_instance as VestTest + + var suite = await test._get_suite() - var results: VestResult.Suite = null - if test_instance is VestTest: - await test_instance._begin(test_instance) - var suite = await test_instance._get_suite() - results = await _run_suite(suite, test_instance) - await test_instance._finish(test_instance) - test_instance.free() + var run_only := false + match only_mode: + Vest.__.ONLY_DISABLED: run_only = false + Vest.__.ONLY_AUTO: run_only = suite.has_only() + Vest.__.ONLY_ENABLED: run_only = true - return results + await test._begin(test_instance) + _result_buffer = await _run_suite(_result_buffer, suite, test, run_only) + await test._finish(test) + + test.free() + + return _result_buffer ## Run test scripts matching glob ## [br][br] ## See [method String.match] -func run_glob(glob: String) -> VestResult.Suite: - var result := VestResult.Suite.new() - result.suite = VestDefs.Suite.new() - result.suite.name = "Glob suite \"%s\"" % [glob] +func run_glob(glob: String, only_mode: int = Vest.__.ONLY_DEFAULT) -> VestResult.Suite: + _result_buffer = VestResult.Suite.new() + _result_buffer.suite = VestDefs.Suite.new() + _result_buffer.suite.name = "Glob suite \"%s\"" % [glob] + + # Gather suites + var suites := [] as Array[VestDefs.Suite] + var test_instances := [] as Array[VestTest] + var has_only = false for test_file in Vest.glob(glob): - var suite_result := await run_script_at(test_file) - if suite_result: - result.subsuites.append(suite_result) + var test := _load_test(test_file) + if not test: continue - return result + var subsuite := await test._get_suite() + test_instances.append(test) + suites.append(subsuite) + has_only = has_only or subsuite.has_only() + + # Figure out only mode + var run_only := false + match only_mode: + Vest.__.ONLY_DISABLED: run_only = false + Vest.__.ONLY_AUTO: run_only = has_only + Vest.__.ONLY_ENABLED: run_only = true + + # Run suites + for i in range(suites.size()): + var suite_result := VestResult.Suite.new() + _result_buffer.subsuites.append(suite_result) + + var response := await _run_suite(suite_result, suites[i], test_instances[i], run_only) + if not response: + _result_buffer.subsuites.erase(suite_result) + + # Cleanup + for test in test_instances: + test.free() + + return _result_buffer + +func _load_test(path: String) -> VestTest: + var script := load(path) + if not script or not script is Script: + return null + + var test_instance = (script as Script).new() + if not test_instance is VestTest: + return null + + return test_instance as VestTest + +func _run_case(case: VestDefs.Case, test_instance: VestTest, run_only: bool, is_parent_only: bool = false) -> VestResult.Case: + if run_only and not case.is_only and not is_parent_only: + return null -func _run_case(case: VestDefs.Case, test_instance: VestTest) -> VestResult.Case: await test_instance._begin(case) await case.callback.call() await test_instance._finish(case) + on_partial_result.emit(_result_buffer) + return test_instance._get_result() -func _run_suite(suite: VestDefs.Suite, test_instance: VestTest) -> VestResult.Suite: - var result := VestResult.Suite.new() +func _run_suite(result: VestResult.Suite, suite: VestDefs.Suite, test_instance: VestTest, run_only: bool, is_parent_only: bool = false) -> VestResult.Suite: + if run_only and not suite.has_only() and not is_parent_only: + return null + result.suite = suite await test_instance._begin(suite) for subsuite in suite.suites: - var suite_result := await _run_suite(subsuite, test_instance) - result.subsuites.append(suite_result) + var suite_result := VestResult.Suite.new() + suite_result = await _run_suite(suite_result, subsuite, test_instance, run_only, is_parent_only or suite.is_only) + if suite_result != null: + result.subsuites.append(suite_result) for case in suite.cases: - var case_result := await _run_case(case, test_instance) - result.cases.append(case_result) + var case_result := await _run_case(case, test_instance, run_only, is_parent_only or suite.is_only) + if case_result != null: + result.cases.append(case_result) await test_instance._finish(suite) diff --git a/trimsock.gd/addons/vest/tap-reporter.gd b/trimsock.gd/addons/vest/tap-reporter.gd index d7c281d..059ef69 100644 --- a/trimsock.gd/addons/vest/tap-reporter.gd +++ b/trimsock.gd/addons/vest/tap-reporter.gd @@ -19,7 +19,7 @@ static func report(suite: VestResult.Suite) -> String: static func _report_suite(suite: VestResult.Suite, lines: PackedStringArray, indent: int = 0): var indent_prefix := " ".repeat(indent) - var test_count := suite.size() + var test_count := suite.plan_size() var test_id := 1 lines.append(indent_prefix + "1..%d" % [test_count]) @@ -55,7 +55,10 @@ static func _report_case(test_id: int, case: VestResult.Case, lines: PackedStrin yaml_data["severity"] = "fail" yaml_data["assert_source"] = case.assert_file yaml_data["assert_line"] = case.assert_line - if case.message: yaml_data["message"] = case.message + if not case.messages.is_empty(): + yaml_data["message"] = case.messages[0] + if case.messages.size() > 1: + yaml_data["messages"] = case.messages if case.data: yaml_data["data"] = case.data if not yaml_data.is_empty(): diff --git a/trimsock.gd/addons/vest/test/mixins/gather-suite-mixin.gd b/trimsock.gd/addons/vest/test/mixins/gather-suite-mixin.gd index cfc1440..528e8e7 100644 --- a/trimsock.gd/addons/vest/test/mixins/gather-suite-mixin.gd +++ b/trimsock.gd/addons/vest/test/mixins/gather-suite-mixin.gd @@ -56,9 +56,18 @@ func _get_suite() -> VestDefs.Suite: await call(method["name"]) for method in case_methods: - test(method["name"].trim_prefix("test").capitalize(), func(): await call(method["name"])) + var method_name := method["name"] as String + var test_name := method_name.trim_prefix("test").trim_suffix("__only").capitalize() + var callback := func(): await call(method["name"]) + var is_only := _is_only(method_name) + + test(test_name, callback, is_only, method_name) for method in parametric_methods: + var method_name := method["name"] as String + var test_name := method_name.trim_prefix("test").trim_suffix("__only").capitalize() + var is_only := _is_only(method_name) + var param_provider_name := method["default_args"][0] as String if not has_method(param_provider_name): push_warning( @@ -75,7 +84,12 @@ func _get_suite() -> VestDefs.Suite: for i in range(params.size()): test( - "%s#%d %s" % [method["name"].trim_prefix("test").capitalize(), i+1, params[i]], - func(): await callv(method["name"], params[i]) + "%s#%d %s" % [test_name, i+1, params[i]], + func(): await callv(method["name"], params[i]), + is_only, + method_name ) ) + +func _is_only(name: String) -> bool: + return name.ends_with("__only") diff --git a/trimsock.gd/addons/vest/test/vest-test-base.gd b/trimsock.gd/addons/vest/test/vest-test-base.gd index 9ee784a..0445a7b 100644 --- a/trimsock.gd/addons/vest/test/vest-test-base.gd +++ b/trimsock.gd/addons/vest/test/vest-test-base.gd @@ -3,6 +3,9 @@ extends Object var _define_stack: Array[VestDefs.Suite] = [] var _result: VestResult.Case +var _expected_count := 0 +var _actual_count := 0 + signal on_begin() signal on_suite_begin(suite: VestDefs.Suite) signal on_case_begin(case: VestDefs.Case) @@ -10,9 +13,10 @@ signal on_case_finish(case: VestDefs.Case) signal on_suite_finish(case: VestDefs.Case) signal on_finish() -func define(name: String, callback: Callable) -> VestDefs.Suite: +func define(name: String, callback: Callable, is_only: bool = false) -> VestDefs.Suite: var suite = VestDefs.Suite.new() suite.name = name + suite.is_only = is_only _define_stack.push_back(suite) var userland_loc := _find_userland_stack_location() @@ -27,10 +31,15 @@ func define(name: String, callback: Callable) -> VestDefs.Suite: return suite -func test(description: String, callback: Callable) -> void: +func define_only(name: String, callback: Callable) -> VestDefs.Suite: + return await define(name, callback, true) + +func test(description: String, callback: Callable, is_only: bool = false, method_name: String = "") -> void: var case_def := VestDefs.Case.new() case_def.description = description + case_def.is_only = is_only case_def.callback = callback + case_def.method_name = method_name var userland_loc := _find_userland_stack_location() case_def.definition_file = userland_loc[0] @@ -38,6 +47,9 @@ func test(description: String, callback: Callable) -> void: _define_stack.back().cases.push_back(case_def) +func test_only(description: String, callback: Callable, method_name: String = "") -> void: + await test(description, callback, true, method_name) + func benchmark(name: String, callback: Callable) -> VestDefs.Benchmark: var result := VestDefs.Benchmark.new() result.name = name @@ -79,13 +91,39 @@ func _init(): pass func _with_result(status: int, message: String, data: Dictionary): + if message: + _result.messages.append(message) + + # Smartly gather "got" and "expected" data + # - If there's just one assert that sends these, have the values as-is + # - If multiple asserts send them, gather them into arrays for the user to check + if data.has("got"): + if _actual_count == 0: + _result.data["got"] = data["got"] + elif _actual_count == 1: + _result.data["got"] = [_result.data["got"], data["got"]] + elif _actual_count > 1: + _result.data["got"].append(data["got"]) + data.erase("got") + _actual_count += 1 + + if data.has("expect"): + if _expected_count == 0: + _result.data["expect"] = data["expect"] + elif _expected_count == 1: + _result.data["expect"] = [_result.data["expect"], data["expect"]] + elif _expected_count > 1: + _result.data["expect"].append(data["expect"]) + data.erase("expect") + _expected_count += 1 + + _result.data.merge(data, true) + if _result.status != VestResult.TEST_VOID and status == VestResult.TEST_PASS: # Test already failed, don't override with PASS return _result.status = status - _result.message = message - _result.data.merge(data, true) var userland_loc := _find_userland_stack_location() _result.assert_file = userland_loc[0] @@ -114,6 +152,9 @@ func _finish(what: Object): await _emit_async(on_suite_finish, [what]) await after_suite(what) elif what is VestDefs.Case: + _actual_count = 0 + _expected_count = 0 + await _emit_async(on_case_finish, [what]) await after_case(what) else: diff --git a/trimsock.gd/addons/vest/test/vest-test.gd b/trimsock.gd/addons/vest/test/vest-test.gd index e8b1913..9abc9d0 100644 --- a/trimsock.gd/addons/vest/test/vest-test.gd +++ b/trimsock.gd/addons/vest/test/vest-test.gd @@ -6,4 +6,4 @@ extends "res://addons/vest/_generated-mixins/6-3c4a4dd7.gd" class_name VestTest func __get_vest_mixins() -> Array: - return [] \ No newline at end of file + return [] diff --git a/trimsock.gd/addons/vest/ui/results-panel.gd b/trimsock.gd/addons/vest/ui/results-panel.gd new file mode 100644 index 0000000..dfc0736 --- /dev/null +++ b/trimsock.gd/addons/vest/ui/results-panel.gd @@ -0,0 +1,292 @@ +@tool +extends Panel + +var visibility_popup: VestUI.VisibilityPopup + +@onready var _tree := %Tree as Tree +@onready var _spinner := %Spinner as Control +@onready var _spinner_icon := %"Spinner Icon" as TextureRect +@onready var _spinner_label := %"Spinner Label" as Label +@onready var _animation_player := $PanelContainer/Spinner/AnimationPlayer as AnimationPlayer + +var _results: VestResult.Suite = null +var _render_results: VestResult.Suite = null +var _search_string: String = "" + +signal on_collapse_changed() + +func get_results() -> VestResult.Suite: + return _results + +func set_results(results: VestResult.Suite) -> void: + if _results != results: + _results = results + _render() + +func get_search_string() -> String: + return _search_string + +func set_search_string(search_string: String) -> void: + if _search_string != search_string: + _search_string = search_string + _render() + +func clear() -> void: + set_results(null) + +func set_spinner(text: String, icon: Texture2D = null, animated: bool = true) -> void: + clear() + _spinner_icon.texture = icon + _spinner_label.text = text + _spinner.show() + if animated: + _animation_player.play("spin") + else: + _animation_player.stop() + +func collapse() -> void: + var root := _tree.get_root() + if not root: return + + for item in root.get_children(): + item.set_collapsed_recursive(true) + +func expand() -> void: + var root := _tree.get_root() + if not root: return + + for item in root.get_children(): + item.set_collapsed_recursive(false) + +func toggle_collapsed() -> void: + if is_any_collapsed(): + expand() + else: + collapse() + +func is_any_collapsed() -> bool: + var at := _tree.get_root() + var queue := [] as Array[TreeItem] + + while at: + queue.append_array(at.get_children()) + + if at.collapsed: + return true + at = queue.pop_back() + + return false + +func _ready(): + _tree.item_collapsed.connect(func(_item): + on_collapse_changed.emit() + ) + +func _clear(): + _tree.clear() + for connection in _tree.item_activated.get_connections(): + connection["signal"].disconnect(connection["callable"]) + _spinner.hide() + +func _render(): + _clear() + if _results != null: + _render_results = VestResult.Suite._from_wire(_results._to_wire()) # HACK: Duplicate + _filter_visibility(_render_results) + _filter_search(_render_results, _search_string) + _render_result(_render_results, _tree) + +func _filter_visibility(results: VestResult.Suite) -> void: + var cases_to_remove := [] + var suites_to_remove := [] + + for case in results.cases: + if not _check_visibility(case): + cases_to_remove.append(case) + + for subsuite in results.subsuites: + if not _check_visibility(subsuite): + suites_to_remove.append(subsuite) + else: + _filter_visibility(subsuite) + + for case in cases_to_remove: + results.cases.erase(case) + for subsuite in suites_to_remove: + results.subsuites.erase(subsuite) + +func _filter_search(results: VestResult.Suite, needle: String) -> void: + if not needle: + # Search string empty, do nothing + return + + var scores := {} + var parents := {} + var at := results + var queue: Array[VestResult.Suite] = [] + var leaves: Array[VestResult.Suite] = [] + + # Calculate scores + while at: + for subsuite in at.subsuites: + queue.append(subsuite) + parents[subsuite] = at + + if at.subsuites.is_empty(): + leaves.append(at) + + scores[at] = VestUI.fuzzy_score(needle, at.suite.name) + for case in at.cases: + scores[case] = maxf( + VestUI.fuzzy_score(needle, case.case.description), + VestUI.fuzzy_score(needle, case.case.method_name + "()") + ) + + at = queue.pop_back() as VestResult.Suite + + # Propagate best scores from leaves + for leaf in leaves: + at = leaf + + # Calculate best score for leaf + var best_score := scores[at] as float + for case in at.cases: + best_score = maxf(best_score, scores[case]) + + # Propagate upwards in tree + while at: + scores[at] = maxf(scores[at], best_score) + best_score = maxf(best_score, scores[at]) + at = parents.get(at, null) + + # Remove results that don't match the search string + at = results + queue.clear() + while at: + at.cases.sort_custom(func(a, b): return scores[a] > scores[b]) + at.subsuites.sort_custom(func(a, b): return scores[a] > scores[b]) + + queue.append_array(at.subsuites) + at = queue.pop_back() + +func _check_visibility(what: Variant) -> bool: + if what is VestResult.Case: + var case := what as VestResult.Case + return visibility_popup.get_visibility_for(case.status) + elif what is VestResult.Suite: + var suite := what as VestResult.Suite + for status in suite.get_unique_statuses(): + if visibility_popup.get_visibility_for(status): + return true + return false + else: + push_warning("Checking visibility for unknown item: %s" % [what]) + return true + +func _render_result(what: Object, tree: Tree, parent: TreeItem = null): + if what is VestResult.Suite: + var item := tree.create_item(parent) + item.set_text(0, what.suite.name) + item.set_text(1, what.get_aggregate_status_string().capitalize()) + + item.set_icon(0, VestUI.get_status_icon(what)) + item.set_icon_max_width(0, VestUI.get_icon_size()) + + tree.item_activated.connect(func(): + if tree.get_selected() == item: + _navigate(what.suite.definition_file, what.suite.definition_line) + ) + + for subsuite in what.subsuites: + _render_result(subsuite, tree, item) + for case in what.cases: + _render_result(case, tree, item) + elif what is VestResult.Case: + var item := tree.create_item(parent) + item.set_text(0, what.case.description) + item.set_text(1, what.get_status_string().capitalize()) + item.collapsed = what.status == VestResult.TEST_PASS + + item.set_icon(0, VestUI.get_status_icon(what)) + item.set_icon_max_width(0, VestUI.get_icon_size()) + + _render_data(what, tree, item) + + tree.item_activated.connect(func(): + if tree.get_selected() == item: + _navigate(what.case.definition_file, what.case.definition_line) + ) + else: + push_error("Rendering unknown object: %s" % [what]) + +func _render_data(case: VestResult.Case, tree: Tree, parent: TreeItem): + var data := case.data.duplicate() + + for message in case.messages: + var item := tree.create_item(parent) + item.set_text(0, message) + + tree.item_activated.connect(func(): + if tree.get_selected() == item: + # TODO: popup_dialog() + add_child(VestMessagePopup.of(message).window) + ) + + if data == null or data.is_empty(): + return + + if data.has("messages"): + var header_item := tree.create_item(parent) + header_item.set_text(0, "Messages") + + for message in data["messages"]: + tree.create_item(header_item).set_text(0, message) + + data.erase("messages") + + if data.has("benchmarks"): + var header_item := tree.create_item(parent) + header_item.set_text(0, "Benchmarks") + + for benchmark in data["benchmarks"]: + var benchmark_item = tree.create_item(header_item) + benchmark_item.set_text(0, benchmark["name"]) + if benchmark.has("duration"): benchmark_item.set_text(1, benchmark["duration"]) + + for measurement in benchmark.keys(): + if measurement == "name": continue + + var measurement_item := tree.create_item(benchmark_item) + measurement_item.set_text(0, str(measurement).capitalize()) + measurement_item.set_text(1, str(benchmark[measurement])) + + data.erase("benchmarks") + + if data.has("expect") and data.has("got"): + var header_item := tree.create_item(parent) + header_item.set_text(0, "Got:") + header_item.set_text(1, "Expected:") + + var got_string := JSON.stringify(data["got"], " ") + var expect_string := JSON.stringify(data["expect"], " ") + + var comparison_item := tree.create_item(header_item) + comparison_item.set_text(0, got_string) + comparison_item.set_text(1, expect_string) + + tree.item_activated.connect(func(): + # TODO: popup_dialog() + if tree.get_selected() in [header_item, comparison_item]: + add_child(VestComparisonPopup.of(expect_string, got_string).window) + ) + + data.erase("got") + data.erase("expect") + + for key in data: + var item := tree.create_item(parent) + item.set_text(0, var_to_str(key)) + item.set_text(1, var_to_str(data[key])) + +func _navigate(file: String, line: int): + Vest._get_editor_interface().edit_script(load(file), line) diff --git a/trimsock.gd/addons/vest/ui/results-panel.gd.uid b/trimsock.gd/addons/vest/ui/results-panel.gd.uid new file mode 100644 index 0000000..08ab771 --- /dev/null +++ b/trimsock.gd/addons/vest/ui/results-panel.gd.uid @@ -0,0 +1 @@ +uid://brlr0bv7le1uq diff --git a/trimsock.gd/addons/vest/ui/results-panel.tscn b/trimsock.gd/addons/vest/ui/results-panel.tscn new file mode 100644 index 0000000..464f302 --- /dev/null +++ b/trimsock.gd/addons/vest/ui/results-panel.tscn @@ -0,0 +1,76 @@ +[gd_scene load_steps=5 format=3 uid="uid://lqudrgj64q3x"] + +[ext_resource type="Script" path="res://addons/vest/ui/results-panel.gd" id="1_ujqyc"] +[ext_resource type="Texture2D" uid="uid://dwhlyf5eyiect" path="res://addons/vest/icons/debug.svg" id="2_1y2jb"] + +[sub_resource type="Animation" id="Animation_wkibd"] +resource_name = "spin" +loop_mode = 1 +step = 0.0416667 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Spinner Icon:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.5, 1), +"transitions": PackedFloat32Array(-2, -2, -2), +"update": 0, +"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0.501961), Color(1, 1, 1, 1)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_ne2nn"] +_data = { +"spin": SubResource("Animation_wkibd") +} + +[node name="Results Panel" type="Panel"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +script = ExtResource("1_ujqyc") + +[node name="PanelContainer" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Tree" type="Tree" parent="PanelContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +columns = 2 +hide_root = true + +[node name="Spinner" type="HBoxContainer" parent="PanelContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 + +[node name="Spinner Icon" type="TextureRect" parent="PanelContainer/Spinner"] +unique_name_in_owner = true +layout_mode = 2 +texture = ExtResource("2_1y2jb") +expand_mode = 3 +stretch_mode = 5 + +[node name="Spinner Label" type="Label" parent="PanelContainer/Spinner"] +unique_name_in_owner = true +layout_mode = 2 +text = "Waiting for results..." + +[node name="AnimationPlayer" type="AnimationPlayer" parent="PanelContainer/Spinner"] +autoplay = "spin" +libraries = { +"": SubResource("AnimationLibrary_ne2nn") +} diff --git a/trimsock.gd/addons/vest/ui/vest-ui.gd b/trimsock.gd/addons/vest/ui/vest-ui.gd index edaf251..60263a7 100644 --- a/trimsock.gd/addons/vest/ui/vest-ui.gd +++ b/trimsock.gd/addons/vest/ui/vest-ui.gd @@ -2,17 +2,31 @@ extends Control class_name VestUI +const VisibilityPopup := preload("res://addons/vest/ui/visibility-popup.gd") +const ResultsPanel := preload("res://addons/vest/ui/results-panel.gd") + @onready var run_all_button := %"Run All Button" as Button @onready var debug_button := %"Debug Button" as Button @onready var run_on_save_button := %"Run on Save Button" as Button +@onready var filter_results_button := %"Filter Results Button" as Button +@onready var visibility_popup := %"Visibility Popup" as VisibilityPopup @onready var clear_button := %"Clear Button" as Button +@onready var expand_toggle_button := %"Expand Toggle Button" as Button +@onready var search_button := %"Search Button" as Button +@onready var search_input := %"Search Input" as LineEdit + @onready var refresh_mixins_button := %"Refresh Mixins Button" as Button -@onready var results_tree := %"Results Tree" as Tree +@onready var results_panel := %"Results Panel" as ResultsPanel @onready var summary_label := %"Tests Summary Label" as Label @onready var summary_icon := %"Test Summary Icon" as TextureRect @onready var glob_line_edit := %"Glob LineEdit" as LineEdit +@onready var run_summary := %"Run Summary" as Control +@onready var progress_indicator := %"Progress Indicator" as Control +@onready var progress_animator := $"VBoxContainer/Bottom Line/Progress Indicator/Control/AnimationPlayer" as AnimationPlayer + var _run_on_save: bool = false +var _results: VestResult.Suite = null static var _icon_size := 16 static var _instance: VestUI @@ -33,13 +47,18 @@ func handle_resource_saved(resource: Resource): func run_all(is_debug: bool = false): Vest._register_scene_tree(get_tree()) var runner := VestDaemonRunner.new() + runner.on_partial_result.connect(func(results): + results_panel.set_results(results) + ) var test_glob := glob_line_edit.text Vest.__.LocalSettings.test_glob = test_glob Vest.__.LocalSettings.flush() - clear_results() - _set_placeholder_text("Waiting for results...") + results_panel.set_spinner("Waiting for results...", Vest.Icons.debug) + progress_indicator.show() + progress_animator.play("spin") + run_summary.hide() var test_start := Vest.time() var results: VestResult.Suite @@ -53,19 +72,23 @@ func run_all(is_debug: bool = false): # Render individual results ingest_results(results, test_duration) -func run_script(script: Script, is_debug: bool = false) -> void: +func run_script(script: Script, is_debug: bool = false, only_mode: int = Vest.__.ONLY_AUTO) -> void: + if not get_tree(): + push_warning("UI has no tree!") Vest._register_scene_tree(get_tree()) var runner := VestDaemonRunner.new() - clear_results() - _set_placeholder_text("Waiting for results...") + results_panel.set_spinner("Waiting for results...", Vest.Icons.debug) + progress_indicator.show() + progress_animator.play("spin") + run_summary.hide() var test_start := Vest.time() var results: VestResult.Suite if not is_debug: - results = await runner.run_script(script) + results = await runner.run_script(script, only_mode) else: - results = await runner.with_debug().run_script(script) + results = await runner.with_debug().run_script(script, only_mode) var test_duration := Vest.time() - test_start @@ -74,21 +97,23 @@ func run_script(script: Script, is_debug: bool = false) -> void: func ingest_results(results: VestResult.Suite, duration: float = -1.) -> void: clear_results() + _results = results + if results: - _render_result(results, results_tree) + results_panel.set_results(results) _render_summary(results, duration) else: - _set_placeholder_text("Test run failed!") + results_panel.set_spinner("Test run failed!", Vest.Icons.result_fail, false) func clear_results(): - results_tree.clear() - for connection in results_tree.item_activated.get_connections(): - connection["signal"].disconnect(connection["callable"]) - + results_panel.clear() summary_label.text = "" summary_icon.visible = false func _ready(): + _icon_size = int(16. * Vest._get_editor_scale()) + results_panel.visibility_popup = visibility_popup + run_all_button.pressed.connect(run_all) run_on_save_button.toggled.connect(func(toggled): _run_on_save = toggled @@ -103,134 +128,63 @@ func _ready(): debug_button.pressed.connect(func(): run_all(true)) - _icon_size = int(16. * Vest._get_editor_interface().get_editor_scale()) - _instance = self + filter_results_button.pressed.connect(func(): + visibility_popup.position = filter_results_button.get_screen_position() + Vector2.RIGHT * filter_results_button.size.x + visibility_popup.show() + ) -func _render_result(what: Object, tree: Tree, parent: TreeItem = null): - if what is VestResult.Suite: - var item := tree.create_item(parent) - item.set_text(0, what.suite.name) - item.set_text(1, what.get_aggregate_status_string().capitalize()) - - item.set_icon(0, _get_status_icon(what)) - item.set_icon_max_width(0, VestUI.get_icon_size()) - - tree.item_activated.connect(func(): - if tree.get_selected() == item: - _navigate(what.suite.definition_file, what.suite.definition_line) - ) - - for subsuite in what.subsuites: - _render_result(subsuite, tree, item) - for case in what.cases: - _render_result(case, tree, item) - elif what is VestResult.Case: - var item := tree.create_item(parent) - item.set_text(0, what.case.description) - item.set_text(1, what.get_status_string().capitalize()) - item.collapsed = what.status == VestResult.TEST_PASS + visibility_popup.on_change.connect(func(): + clear_results() + results_panel.set_results(_results) - item.set_icon(0, _get_status_icon(what)) - item.set_icon_max_width(0, VestUI.get_icon_size()) + if visibility_popup.is_empty(): + filter_results_button.icon = Vest.Icons.hidden + else: + filter_results_button.icon = Vest.Icons.visible + ) - _render_data(what, tree, item) + results_panel.on_collapse_changed.connect(func(): + if (results_panel.is_any_collapsed()): + expand_toggle_button.icon = Vest.Icons.expand + else: + expand_toggle_button.icon = Vest.Icons.collapse + ) - tree.item_activated.connect(func(): - if tree.get_selected() == item: - _navigate(what.case.definition_file, what.case.definition_line) - ) - else: - push_error("Rendering unknown object: %s" % [what]) + expand_toggle_button.pressed.connect(func(): + results_panel.toggle_collapsed() + ) + + search_button.pressed.connect(func(): + search_input.show() + search_input.grab_focus() + ) + + search_input.focus_exited.connect(func(): + if not search_input.text and get_viewport().gui_get_focus_owner() != search_button: + search_input.hide() + , CONNECT_DEFERRED) + + search_input.text_changed.connect(func(text: String): + results_panel.set_search_string(text) + ) + + _instance = self func _render_summary(results: VestResult.Suite, test_duration: float): + progress_indicator.hide() + run_summary.show() + if test_duration > 0: - summary_label.text = "Ran %d tests in %.2fms" % [results.size(), test_duration * 1000.] + summary_label.text = "Ran %d tests in %s" % [results.size(), VestUI.format_duration(test_duration)] else: summary_label.text = "Ran %d tests" % [results.size()] summary_icon.visible = true - summary_icon.texture = _get_status_icon(results) + summary_icon.texture = VestUI.get_status_icon(results) summary_icon.custom_minimum_size = Vector2i.ONE * VestUI.get_icon_size() # TODO: Check -func _render_data(case: VestResult.Case, tree: Tree, parent: TreeItem): - var data := case.data.duplicate() - - if case.message: - var item := tree.create_item(parent) - item.set_text(0, case.message) - - tree.item_activated.connect(func(): - if tree.get_selected() == item: - # TODO: popup_dialog() - add_child(VestMessagePopup.of(case.message).window) - ) - - if data == null or data.is_empty(): - return - - if data.has("messages"): - var header_item := tree.create_item(parent) - header_item.set_text(0, "Messages") - - for message in data["messages"]: - tree.create_item(header_item).set_text(0, message) - - data.erase("messages") - - if data.has("benchmarks"): - var header_item := tree.create_item(parent) - header_item.set_text(0, "Benchmarks") - - for benchmark in data["benchmarks"]: - var benchmark_item = tree.create_item(header_item) - benchmark_item.set_text(0, benchmark["name"]) - if benchmark.has("duration"): benchmark_item.set_text(1, benchmark["duration"]) - - for measurement in benchmark.keys(): - if measurement == "name": continue - - var measurement_item := tree.create_item(benchmark_item) - measurement_item.set_text(0, str(measurement).capitalize()) - measurement_item.set_text(1, str(benchmark[measurement])) - - data.erase("benchmarks") - - if data.has("expect") and data.has("got"): - var header_item := tree.create_item(parent) - header_item.set_text(0, "Got:") - header_item.set_text(1, "Expected:") - - var got_string := JSON.stringify(data["got"], " ") - var expect_string := JSON.stringify(data["expect"], " ") - - var comparison_item := tree.create_item(header_item) - comparison_item.set_text(0, got_string) - comparison_item.set_text(1, expect_string) - - tree.item_activated.connect(func(): - # TODO: popup_dialog() - if tree.get_selected() in [header_item, comparison_item]: - add_child(VestComparisonPopup.of(expect_string, got_string).window) - ) - - data.erase("got") - data.erase("expect") - - for key in data: - var item := tree.create_item(parent) - item.set_text(0, var_to_str(key)) - item.set_text(1, var_to_str(data[key])) - -func _set_placeholder_text(text: String): - results_tree.clear() - var placeholder_root := results_tree.create_item() - results_tree.create_item(placeholder_root).set_text(0, text) - -func _navigate(file: String, line: int): - Vest._get_editor_interface().edit_script(load(file), line) - -func _get_status_icon(what: Variant) -> Texture2D: +static func get_status_icon(what: Variant) -> Texture2D: if what is VestResult.Suite: - return _get_status_icon(what.get_aggregate_status()) + return get_status_icon(what.get_aggregate_status()) elif what is VestResult.Case: if what.data.has("benchmarks"): if what.status == VestResult.TEST_FAIL: @@ -238,7 +192,7 @@ func _get_status_icon(what: Variant) -> Texture2D: else: return Vest.Icons.benchmark else: - return _get_status_icon(what.status) + return get_status_icon(what.status) elif what is int: match(what): VestResult.TEST_VOID: return Vest.Icons.result_void @@ -247,3 +201,25 @@ func _get_status_icon(what: Variant) -> Texture2D: VestResult.TEST_FAIL: return Vest.Icons.result_fail VestResult.TEST_PASS: return Vest.Icons.result_pass return null + +static func format_duration(duration: float) -> String: + if duration > 60.: + return "%.2fmin" % duration + elif duration > 1.: + return "%.2fs" % duration + elif duration > 0.001: + return "%.2fms" % [duration * 1000.] + else: + return "%.2fµs" % [duration * 1000_000.0] + +static func fuzzy_score(needle: String, haystack: String) -> float: + var ineedle := needle.to_lower() + var ihaystack := haystack.to_lower() + return ineedle.similarity(ihaystack) + float(ineedle.is_subsequence_of(ihaystack)) + +static func fuzzy_match(needle: String, haystack: String) -> bool: + return fuzzy_score(needle, haystack) > 0.0 + +static func fuzzy_sorter(needle: String) -> Callable: + return func(a, b): + return fuzzy_score(needle, a) < fuzzy_score(needle, b) diff --git a/trimsock.gd/addons/vest/ui/vest-ui.tscn b/trimsock.gd/addons/vest/ui/vest-ui.tscn index 7b1ee96..c9247c2 100644 --- a/trimsock.gd/addons/vest/ui/vest-ui.tscn +++ b/trimsock.gd/addons/vest/ui/vest-ui.tscn @@ -1,12 +1,40 @@ -[gd_scene load_steps=8 format=3 uid="uid://bp8g7j7774mwi"] +[gd_scene load_steps=16 format=3 uid="uid://bp8g7j7774mwi"] -[ext_resource type="Script" uid="uid://i36by1fk3oss" path="res://addons/vest/ui/vest-ui.gd" id="1_cct3x"] +[ext_resource type="Script" path="res://addons/vest/ui/vest-ui.gd" id="1_cct3x"] [ext_resource type="Texture2D" uid="uid://r4y6ihamgino" path="res://addons/vest/icons/run.svg" id="2_q3lni"] +[ext_resource type="PackedScene" uid="uid://lqudrgj64q3x" path="res://addons/vest/ui/results-panel.tscn" id="2_r41u0"] [ext_resource type="Texture2D" uid="uid://dwhlyf5eyiect" path="res://addons/vest/icons/debug.svg" id="3_mcbjt"] [ext_resource type="Texture2D" uid="uid://ctdipwc8sklwo" path="res://addons/vest/icons/clear.svg" id="4_rjham"] [ext_resource type="Texture2D" uid="uid://dt75n55vr4kq0" path="res://addons/vest/icons/refresh.svg" id="5_k7hxl"] [ext_resource type="Texture2D" uid="uid://bmqjme87oq3xx" path="res://addons/vest/icons/pass.svg" id="6_mef67"] [ext_resource type="Texture2D" uid="uid://c711af0y1s2ct" path="res://addons/vest/icons/run-save.svg" id="6_wy3v7"] +[ext_resource type="Texture2D" uid="uid://bpyua1ic4p47h" path="res://addons/vest/icons/visibility.svg" id="6_xkbs5"] +[ext_resource type="Texture2D" uid="uid://sx0233b14cll" path="res://addons/vest/icons/expand.svg" id="9_hg4sy"] +[ext_resource type="Texture2D" uid="uid://cl6im4a1uj6nq" path="res://addons/vest/icons/search.svg" id="10_2uogy"] +[ext_resource type="Texture2D" uid="uid://csfyamlc15vp0" path="res://addons/vest/icons/spinner.svg" id="10_7lh4k"] +[ext_resource type="PackedScene" uid="uid://dbgbcglqh42ya" path="res://addons/vest/ui/visibility-popup.tscn" id="13_72bup"] + +[sub_resource type="Animation" id="Animation_cyqg3"] +resource_name = "spin" +loop_mode = 1 +step = 0.0416667 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Spinner:rotation") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(0.5, 1), +"update": 0, +"values": [0.0, 6.28319] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_ed1ow"] +_data = { +"spin": SubResource("Animation_cyqg3") +} [node name="Vest UI" type="Control"] custom_minimum_size = Vector2(0, 144) @@ -27,30 +55,17 @@ grow_horizontal = 2 grow_vertical = 2 size_flags_horizontal = 3 -[node name="Results Panel" type="Panel" parent="VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer/Results Panel"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="Results Tree" type="Tree" parent="VBoxContainer/Results Panel/PanelContainer"] +[node name="Results Panel" parent="VBoxContainer" instance=ExtResource("2_r41u0")] unique_name_in_owner = true layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -columns = 2 -hide_root = true [node name="Bottom Line" type="HBoxContainer" parent="VBoxContainer"] layout_mode = 2 -[node name="Run All Button" type="Button" parent="VBoxContainer/Bottom Line"] +[node name="Buttons" type="HBoxContainer" parent="VBoxContainer/Bottom Line"] +layout_mode = 2 + +[node name="Run All Button" type="Button" parent="VBoxContainer/Bottom Line/Buttons"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Run tests" @@ -59,7 +74,7 @@ icon = ExtResource("2_q3lni") icon_alignment = 1 expand_icon = true -[node name="Debug Button" type="Button" parent="VBoxContainer/Bottom Line"] +[node name="Debug Button" type="Button" parent="VBoxContainer/Bottom Line/Buttons"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Debug tests" @@ -68,7 +83,7 @@ icon = ExtResource("3_mcbjt") icon_alignment = 1 expand_icon = true -[node name="Clear Button" type="Button" parent="VBoxContainer/Bottom Line"] +[node name="Clear Button" type="Button" parent="VBoxContainer/Bottom Line/Buttons"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Clear results" @@ -77,7 +92,7 @@ icon = ExtResource("4_rjham") icon_alignment = 1 expand_icon = true -[node name="Run on Save Button" type="Button" parent="VBoxContainer/Bottom Line"] +[node name="Run on Save Button" type="Button" parent="VBoxContainer/Bottom Line/Buttons"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Run on Save" @@ -87,36 +102,122 @@ icon = ExtResource("6_wy3v7") icon_alignment = 1 expand_icon = true +[node name="Filter Results Button" type="Button" parent="VBoxContainer/Bottom Line/Buttons"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Filter results" +text = " " +icon = ExtResource("6_xkbs5") +icon_alignment = 1 +expand_icon = true + +[node name="Visibility Popup" parent="VBoxContainer/Bottom Line/Buttons/Filter Results Button" instance=ExtResource("13_72bup")] +unique_name_in_owner = true +visible = false + +[node name="Search Button" type="Button" parent="VBoxContainer/Bottom Line/Buttons"] +unique_name_in_owner = true +layout_mode = 2 +text = " " +icon = ExtResource("10_2uogy") +icon_alignment = 1 +expand_icon = true + +[node name="Search Input" type="LineEdit" parent="VBoxContainer/Bottom Line/Buttons"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(192, 0) +layout_mode = 2 +placeholder_text = "Search..." + +[node name="Expand Toggle Button" type="Button" parent="VBoxContainer/Bottom Line/Buttons"] +unique_name_in_owner = true +layout_mode = 2 +text = " " +icon = ExtResource("9_hg4sy") +icon_alignment = 1 +expand_icon = true + [node name="VSeparator3" type="VSeparator" parent="VBoxContainer/Bottom Line"] layout_mode = 2 -[node name="Test Summary Icon" type="TextureRect" parent="VBoxContainer/Bottom Line"] +[node name="Run Summary" type="HBoxContainer" parent="VBoxContainer/Bottom Line"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Test Summary Icon" type="TextureRect" parent="VBoxContainer/Bottom Line/Run Summary"] unique_name_in_owner = true visible = false +custom_minimum_size = Vector2(16, 16) layout_mode = 2 size_flags_vertical = 4 texture = ExtResource("6_mef67") expand_mode = 5 stretch_mode = 4 -[node name="Tests Summary Label" type="Label" parent="VBoxContainer/Bottom Line"] +[node name="Tests Summary Label" type="Label" parent="VBoxContainer/Bottom Line/Run Summary"] unique_name_in_owner = true custom_minimum_size = Vector2(220, 0) layout_mode = 2 -text = "Ready" + +[node name="Progress Indicator" type="HBoxContainer" parent="VBoxContainer/Bottom Line"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Control" type="Control" parent="VBoxContainer/Bottom Line/Progress Indicator"] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 + +[node name="Spinner" type="TextureRect" parent="VBoxContainer/Bottom Line/Progress Indicator/Control"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -16.0 +offset_top = -15.5 +offset_right = 16.0 +offset_bottom = 16.5 +grow_horizontal = 2 +grow_vertical = 2 +rotation = 2.9185 +pivot_offset = Vector2(16, 16) +texture = ExtResource("10_7lh4k") +expand_mode = 5 +stretch_mode = 5 +flip_h = true + +[node name="AnimationPlayer" type="AnimationPlayer" parent="VBoxContainer/Bottom Line/Progress Indicator/Control"] +autoplay = "spin" +libraries = { +"": SubResource("AnimationLibrary_ed1ow") +} + +[node name="Label" type="Label" parent="VBoxContainer/Bottom Line/Progress Indicator"] +layout_mode = 2 +text = "Running tests..." [node name="VSeparator2" type="VSeparator" parent="VBoxContainer/Bottom Line"] layout_mode = 2 -[node name="Glob Label" type="Label" parent="VBoxContainer/Bottom Line"] +[node name="Glob" type="HBoxContainer" parent="VBoxContainer/Bottom Line"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 3.0 + +[node name="Glob Label" type="Label" parent="VBoxContainer/Bottom Line/Glob"] layout_mode = 2 text = "Test glob:" -[node name="Glob LineEdit" type="LineEdit" parent="VBoxContainer/Bottom Line"] +[node name="Glob LineEdit" type="LineEdit" parent="VBoxContainer/Bottom Line/Glob"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -text = "res://*.test.gd" +text = "res://tests/*.test.gd" [node name="VSeparator4" type="VSeparator" parent="VBoxContainer/Bottom Line"] layout_mode = 2 diff --git a/trimsock.gd/addons/vest/ui/visibility-popup.gd b/trimsock.gd/addons/vest/ui/visibility-popup.gd new file mode 100644 index 0000000..65f3c42 --- /dev/null +++ b/trimsock.gd/addons/vest/ui/visibility-popup.gd @@ -0,0 +1,70 @@ +@tool +extends PopupPanel + +@onready var _container := %"Statuses Container" as Control +@onready var _toggle_button := %"Toggle Button" as Button + +var _visibilities: Dictionary = {} + +signal on_change() + +func get_visibility_for(status: int) -> bool: + return _visibilities.get(status, true) + +func is_empty() -> bool: + # Return true if all visibilites are set to false + for status in _visibilities: + if _visibilities[status]: + return false + return true + +func _init() -> void: + # Default visibility to true + for status in range(VestResult.TEST_MAX): + _visibilities[status] = true + +func _ready() -> void: + _render() + _toggle_button.pressed.connect(_toggle_all) + +func _toggle_all() -> void: + var visibility := is_empty() + for status in _visibilities.keys(): + _visibilities[status] = visibility + _render() + on_change.emit() + +func _render() -> void: + if _container.get_child_count() != _visibilities.size(): + # Remove children + for child in _container.get_children(): + child.queue_free() + + for status in _visibilities.keys(): + var checkbox := CheckBox.new() + checkbox.toggle_mode = true + checkbox.text = VestResult.get_status_string(status).capitalize() + checkbox.icon = VestUI.get_status_icon(status) + checkbox.expand_icon = true + + checkbox.pressed.connect(func(): + _visibilities[status] = not _visibilities[status] + _render() + on_change.emit() + ) + + _container.add_child(checkbox) + + # Update checkbox statuses + for idx in range(_container.get_child_count()): + var status := _visibilities.keys()[idx] as int + var checkbox := _container.get_child(idx) as CheckBox + checkbox.set_pressed_no_signal(_visibilities[status]) + + # Update toggle button + if is_empty(): + _toggle_button.text = "All" + _toggle_button.icon = Vest.Icons.visible + else: + _toggle_button.text = "None" + _toggle_button.icon = Vest.Icons.hidden diff --git a/trimsock.gd/addons/vest/ui/visibility-popup.gd.uid b/trimsock.gd/addons/vest/ui/visibility-popup.gd.uid new file mode 100644 index 0000000..07f495d --- /dev/null +++ b/trimsock.gd/addons/vest/ui/visibility-popup.gd.uid @@ -0,0 +1 @@ +uid://hnnddmijt1mg diff --git a/trimsock.gd/addons/vest/ui/visibility-popup.tscn b/trimsock.gd/addons/vest/ui/visibility-popup.tscn new file mode 100644 index 0000000..fe3bc99 --- /dev/null +++ b/trimsock.gd/addons/vest/ui/visibility-popup.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=4 format=3 uid="uid://dbgbcglqh42ya"] + +[ext_resource type="Script" path="res://addons/vest/ui/visibility-popup.gd" id="1_21m7j"] +[ext_resource type="Texture2D" uid="uid://buuq6qptg3vll" path="res://addons/vest/icons/hidden.svg" id="2_urx7m"] + +[sub_resource type="Theme" id="Theme_l01ra"] +Button/colors/icon_normal_color = Color(1, 1, 1, 0.501961) +Button/colors/icon_pressed_color = Color(1, 1, 1, 1) + +[node name="Visibility Popup" type="PopupPanel"] +size = Vector2i(128, 214) +visible = true +theme = SubResource("Theme_l01ra") +script = ExtResource("1_21m7j") + +[node name="Buttons Container" type="VBoxContainer" parent="."] +offset_left = 4.0 +offset_top = 4.0 +offset_right = 124.0 +offset_bottom = 210.0 + +[node name="Statuses Container" type="VBoxContainer" parent="Buttons Container"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Toggle Button" type="Button" parent="Buttons Container"] +unique_name_in_owner = true +layout_mode = 2 +text = "None" +icon = ExtResource("2_urx7m") +expand_icon = true diff --git a/trimsock.gd/addons/vest/vest-defs.gd b/trimsock.gd/addons/vest/vest-defs.gd index d042c34..fc62ff1 100644 --- a/trimsock.gd/addons/vest/vest-defs.gd +++ b/trimsock.gd/addons/vest/vest-defs.gd @@ -23,6 +23,9 @@ class Suite: ## Nested test suites contained in the suite var suites: Array[VestDefs.Suite] = [] + ## If true, only this group of tests should run + var is_only: bool = false + ## The resource path to the script that defined the suite var definition_file: String = "" @@ -30,6 +33,15 @@ class Suite: ## Set to -1 for undetermined. var definition_line: int = -1 + ## Return true if either this suite, one of its cases, or one of its + ## subsuites is marked as only. + func has_only() -> bool: + if is_only: + return true + + return cases.any(func(it): return it.is_only) or\ + suites.any(func(it): return it.has_only()) + ## Get the number of test cases in the suite.[br] ## Includes the number of test cases in the suite, and recursively sums up ## the test cases in any of the nested suites. @@ -54,6 +66,12 @@ class Case: ## Test case description, displayed in reports var description: String = "" + ## The method defining the test case - empty if defined via [method VestTest.test] + var method_name: String = "" + + ## If true, only this test should run + var is_only: bool = false + ## The method called to run the test case var callback: Callable @@ -70,6 +88,7 @@ class Case: func _to_wire() -> Dictionary: return { "description": description, + "method_name": method_name, "definition_file": definition_file, "definition_line": definition_line } @@ -78,6 +97,7 @@ class Case: var result := Case.new() result.description = data["description"] + result.method_name = data["method_name"] result.definition_file = data["definition_file"] result.definition_line = data["definition_line"] @@ -231,10 +251,10 @@ class Benchmark: var batch_threshold := 0.001 # 1ms if avg_batch_time <= batch_threshold and _enable_builtin_measures: Vest.message(( - "Benchmark \"%s\" has run with an average of %.6fms per batch. " + + "Benchmark \"%s\" has run with an average of %s per batch. " + "This is probably faster than what can be reliably measured. " + "To avoid this warning, increase the batch size to at least %s.") % - [name, avg_batch_time * 1000., ceil(_batch_size * batch_threshold / avg_batch_time * 1.1)] + [name, VestUI.format_duration(avg_batch_time), ceil(_batch_size * batch_threshold / avg_batch_time * 1.1)] ) return self @@ -306,9 +326,9 @@ class Benchmark: # Add builtin measures if _enable_builtin_measures: result["iterations"] = _iterations - result["duration"] = "%.4fms" % [_duration * 1000.0] + result["duration"] = VestUI.format_duration(_duration) result["iters/sec"] = get_iters_per_sec() - result["average iteration time"] = "%.4fms" % [get_avg_iteration_time() * 1000.0] + result["average iteration time"] = VestUI.format_duration(get_avg_iteration_time()) # Add benchmark data result["name"] = name diff --git a/trimsock.gd/addons/vest/vest-internals.gd b/trimsock.gd/addons/vest/vest-internals.gd index 2f1b2ae..f2751d3 100644 --- a/trimsock.gd/addons/vest/vest-internals.gd +++ b/trimsock.gd/addons/vest/vest-internals.gd @@ -9,6 +9,11 @@ const GoToTestCommand := preload("res://addons/vest/commands/go-to-test-command. const CreateTestCommand := preload("res://addons/vest/commands/create-test-command.gd") const RunTestCommand := preload("res://addons/vest/commands/run-test-command.gd") +const ONLY_DISABLED := 0 +const ONLY_AUTO := 1 +const ONLY_ENABLED := 2 +const ONLY_DEFAULT := ONLY_AUTO + static func create_commands() -> Array[Node]: # TODO: Don't recreate if exists var commands := [ diff --git a/trimsock.gd/addons/vest/vest-result.gd b/trimsock.gd/addons/vest/vest-result.gd index eb4cf3d..d3a7c6e 100644 --- a/trimsock.gd/addons/vest/vest-result.gd +++ b/trimsock.gd/addons/vest/vest-result.gd @@ -10,7 +10,8 @@ enum { ## Result status enum TEST_TODO, ## Test is not implemented yet TEST_FAIL, ## Test has failed TEST_SKIP, ## Test was skipped - TEST_PASS ## Test passed + TEST_PASS, ## Test passed + TEST_MAX ## Represents the size of the result status enum } ## Test suite results. @@ -26,10 +27,18 @@ class Suite: ## Get the number of test cases in the suite.[br] ## Includes the number of test cases in the suite, and recursively sums up - ## the test cases in any of the nested suites. + ## the test cases in any of the nested suites.[br] + ## To count only the direct descendants, see [method plan_size]. func size() -> int: return cases.size() + subsuites.reduce(func(acc, it): return acc + it.size(), 0) + ## Return the number of items in the test plan.[br] + ## Includes the number of test cases and subsuites in the suite. As opposed + ## to [method size], this method doesn't include the test cases of the + ## subsuites. + func plan_size() -> int: + return cases.size() + subsuites.size() + ## Get the aggregate result of the test cases and suites contained in the ## suite. func get_aggregate_status() -> int: @@ -44,6 +53,18 @@ class Suite: func get_aggregate_status_string() -> String: return VestResult.get_status_string(get_aggregate_status()) + ## Get an array of result statuses contained in the suite. + func get_unique_statuses() -> Array[int]: + var result := [] as Array[int] + for kase in cases: + if not result.has(kase.status): + result.push_back(kase.status) + for subsuite in subsuites: + for status in subsuite.get_unique_statuses(): + if not result.has(status): + result.push_back(status) + return result + ## Get the count of test cases and nested suites with the given result ## status. ## [br][br] @@ -78,8 +99,8 @@ class Case: ## The resulting status of the test run. var status: int = TEST_VOID - ## The message attached to this result. - var message: String = "" + # The messages attached to this result + var messages: Array[String] = [] ## Custom data attached to this result. var data: Dictionary = {} @@ -99,7 +120,7 @@ class Case: return { "case": case._to_wire(), "status": status, - "message": message, + "messages": messages.duplicate(), "data": Vest.__.Serializer.serialize(data), "assert_file": assert_file, "assert_line": assert_line @@ -110,7 +131,7 @@ class Case: result.case = VestDefs.Case._from_wire(p_data["case"]) result.status = p_data["status"] - result.message = p_data["message"] + result.messages.assign(p_data["messages"]) result.data = p_data["data"] result.assert_file = p_data["assert_file"] result.assert_line = p_data["assert_line"] diff --git a/trimsock.gd/addons/vest/vest-singleton.gd b/trimsock.gd/addons/vest/vest-singleton.gd index 0f3644e..f15030c 100644 --- a/trimsock.gd/addons/vest/vest-singleton.gd +++ b/trimsock.gd/addons/vest/vest-singleton.gd @@ -86,7 +86,7 @@ static func sleep(duration: float = 0.) -> Error: ## If the test runner doesn't finish running the tests in this time, it's ## considered stuck. This value is read from the project settings. ## [br][br] -## For more info, see the +## For more info, see the ## [url=https://foxssake.github.io/vest/latest/user-guide/project-settings/#runner-timeout]user ## guide[/url]. static func get_runner_timeout() -> float: @@ -98,7 +98,7 @@ static func get_runner_timeout() -> float: ## for deriving paths to test suites. This value is read from the project ## settings. ## [br][br] -## For more info, see the +## For more info, see the ## [url=https://foxssake.github.io/vest/latest/user-guide/project-settings/#sources-root]user ## guide[/url]. static func get_sources_root() -> String: @@ -109,7 +109,7 @@ static func get_sources_root() -> String: ## This is directory is assumed root for all the test suites, and is used ## for deriving paths. This value is read from the project settings. ## [br][br] -## For more info, see the +## For more info, see the ## [url=https://foxssake.github.io/vest/latest/user-guide/project-settings/#tests-root]user ## guide[/url]. static func get_tests_root() -> String: @@ -120,7 +120,7 @@ static func get_tests_root() -> String: ## These are used to recognize and generate filenames for test suites. This ## value is read from the project settings. ## [br][br] -## For more info, see the +## For more info, see the ## [url=https://foxssake.github.io/vest/latest/user-guide/project-settings/#test-name-patterns]user ## guide[/url]. static func get_test_name_patterns() -> Array[FilenamePattern]: @@ -135,7 +135,7 @@ static func get_test_name_patterns() -> Array[FilenamePattern]: ## ## This setting determines what path to suggest when creating new tests. ## [br][br] -## For more info, see the +## For more info, see the ## [url=https://foxssake.github.io/vest/latest/user-guide/project-settings/#new-test-location] ## user guide[/url]. static func get_new_test_location_preference() -> int: @@ -198,6 +198,11 @@ static func glob(pattern: String, max_iters: int = 131072) -> Array[String]: return results +## Returns a [SceneTree]. May be used for tests requiring more complex node +## setups. +static func get_tree() -> SceneTree: + return _scene_tree + static func _clear_messages(): _messages.clear() @@ -209,12 +214,21 @@ static func _register_scene_tree(scene_tree: SceneTree): # HACK: Godot script compiler fails in 4.2+ if the return type is an engine singleton # The best we can do is return an object and assume the methods exist. -# Casting is out of the question as well, also freaks out the script compiler. +# Casting isn't possible either, also freaks out the script compiler. static func _get_editor_interface() -> Object: if Engine.get_version_info().hex >= 0x040200: return Engine.get_singleton("EditorInterface") - else: + elif _editor_interface_provider.is_valid(): return _editor_interface_provider.call() + else: + return null + +static func _get_editor_scale() -> float: + var interface := _get_editor_interface() + if interface and interface.has_method("get_editor_scale"): + return interface.get_editor_scale() + else: + return 1.0 static func _register_editor_interface_provider(provider: Callable): _editor_interface_provider = provider diff --git a/trimsock.gd/tests/incremental_id_generator.perf.gd b/trimsock.gd/tests/incremental_id_generator.perf.gd index c4fcb7c..7387cd6 100644 --- a/trimsock.gd/tests/incremental_id_generator.perf.gd +++ b/trimsock.gd/tests/incremental_id_generator.perf.gd @@ -7,7 +7,7 @@ func test_sequence(): var generator := IncrementalTrimsockIDGenerator.new() var min_seq := 1_000_000 var ids := {} - + benchmark("Generator", func(__): var id := generator.get_id() ids[id] = true diff --git a/trimsock.gd/tests/random_id_generator.perf.gd b/trimsock.gd/tests/random_id_generator.perf.gd index aa80a0f..9730049 100644 --- a/trimsock.gd/tests/random_id_generator.perf.gd +++ b/trimsock.gd/tests/random_id_generator.perf.gd @@ -7,7 +7,7 @@ func test_sequence(): var generator := RandomTrimsockIDGenerator.new() var min_seq := 1_000_000 var ids := {} - + benchmark("Generator", func(__): var id := generator.get_id() ids[id] = true