diff --git a/.github/workflows/gut.yml b/.github/workflows/gut.yml new file mode 100644 index 00000000..f0a0c255 --- /dev/null +++ b/.github/workflows/gut.yml @@ -0,0 +1,38 @@ +name: GUT + +on: + push: + branches-ignore: + - main + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + GUT_Tests: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + - name: Check out personal godot-tester repository + uses: actions/checkout@v3 + with: + repository: db0/godot-tester + path: ./.github/actions/godot-tester + # Runs a single command using the runners shell + - name: Godot Tester + uses: ./.github/actions/godot-tester + with: + version: 3.4.4 + # should be long enough for asset import files to get generated + import-time: 10 + assert-check: true + max-fails: 0 + # How long the test should be run before it's timed out and fails + test-timeout: 900 + # Directory containing Gut tests + direct-scene: tests/cli/tests.tscn \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e0646b52..dd27acbb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,33 +1,67 @@ -name: GUT +name: Prepare New Release on: - push: {} - pull_request: {} + push: + branches: + - main - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: -# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - GUT_Tests: - # The type of runner that the job will run on + GUT: runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job + name: Unit & Integration Tests steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - + - name: "✔️ Checkout" + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Check out personal godot-tester repository + uses: actions/checkout@v3 + with: + repository: db0/godot-tester + path: ./.github/actions/godot-tester # Runs a single command using the runners shell - - name: Godot Tester - uses: croconut/godot-tester@v2.4 - with: - version: 3.4 - # should be long enough for asset import files to get generated - import-time: 10 - assert-check: true - max-fails: 0 - # How long the test should be run before it's timed out and fails - test-timeout: 900 - # Directory containing Gut tests - direct-scene: tests/cli/tests.tscn \ No newline at end of file + - name: ⚙ Run Tests + uses: ./.github/actions/godot-tester + with: + version: 3.4.4 + # should be long enough for asset import files to get generated + import-time: 300 + assert-check: true + # Allowing some fails on push, as sometimes randomly some asserts might false negative and I haven't yet located the precice reason for this inconsistency + max-fails: 0 + # How long the test should be run before it's timed out and fails + test-timeout: 3600 + # Directory containing Gut tests + direct-scene: tests/cli/tests.tscn + version_and_release: + runs-on: ubuntu-latest + name: Export Game + needs: ["GUT"] + steps: + - name: "✔️ Checkout" + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: "🤖 Export game" + uses: firebelley/godot-export@v3.0.0 + with: + godot_executable_download_url: https://downloads.tuxfamily.org/godotengine/3.4/Godot_v3.4-stable_linux_headless.64.zip + godot_export_templates_download_url: https://downloads.tuxfamily.org/godotengine/3.4/Godot_v3.4-stable_export_templates.tpz + relative_project_path: ./ + base_version: Demo + create_release: false + archive_export_output: true + - name: "🛠 Prepare files for publishing" + shell: bash + run: | + cp -v /home/runner/.local/share/godot/dist/* . + - name: "🚀 Upload HTML5 version to itch.io" + uses: josephbmanley/butler-publish-itchio-action@master + env: + BUTLER_CREDENTIALS: "${{ secrets.BUTLER_CREDENTIALS }}" + CHANNEL: HTML5 + ITCH_GAME: card-game-framework + ITCH_USER: dbzer0 + PACKAGE: HTML5.zip + VERSION: Demo diff --git a/.gitignore b/.gitignore index 63d2978f..1a159e94 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ .import/ *.import export.cfg -export_presets.cfg *.TMP # Mono-specific ignores diff --git a/.gut_editor_config.json b/.gut_editor_config.json new file mode 100644 index 00000000..0a06edaa --- /dev/null +++ b/.gut_editor_config.json @@ -0,0 +1,40 @@ +{ + "background_color": "ff343434", + "config_file": "res://.gutconfig.json", + "dirs": [ + "res://tests/unit", + "res://tests/integration" + ], + "disable_colors": false, + "double_strategy": "partial", + "font_color": "ffc1bfce", + "font_name": "CourierPrime", + "font_size": 15, + "hide_orphans": false, + "ignore_pause": true, + "include_subdirs": false, + "inner_class": "TestModifyProperties", + "junit_xml_file": "", + "junit_xml_timestamp": false, + "log_level": 3, + "opacity": 70, + "post_run_script": "", + "pre_run_script": "", + "prefix": "test_", + "selected": "test_scripting_engine_tasks_modify_properties.gd", + "should_exit": false, + "should_exit_on_success": false, + "should_maximize": true, + "compact_mode": false, + "show_help": false, + "suffix": ".gd", + "tests": [ + + ], + "unit_test_name": "test_modify_properties", + "gut_on_top": true, + "panel_options": { + "font_name": "CourierPrime", + "font_size": 30 + } +} \ No newline at end of file diff --git a/.gut_editor_shortcuts.cfg b/.gut_editor_shortcuts.cfg new file mode 100644 index 00000000..7e6e72ef --- /dev/null +++ b/.gut_editor_shortcuts.cfg @@ -0,0 +1,17 @@ +[main] + +run_all=Object(ShortCut,"resource_local_to_scene":false,"resource_name":"","shortcut":Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":false,"pressed":false,"scancode":49,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +,"script":null) + +run_current_script=Object(ShortCut,"resource_local_to_scene":false,"resource_name":"","shortcut":Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":false,"pressed":false,"scancode":50,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +,"script":null) + +run_current_inner=Object(ShortCut,"resource_local_to_scene":false,"resource_name":"","shortcut":Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":false,"pressed":false,"scancode":51,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +,"script":null) + +run_current_test=Object(ShortCut,"resource_local_to_scene":false,"resource_name":"","shortcut":Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":false,"pressed":false,"scancode":52,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +,"script":null) + +panel_button=Object(ShortCut,"resource_local_to_scene":false,"resource_name":"","shortcut":Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":false,"pressed":false,"scancode":48,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +,"script":null) + diff --git a/BigFont.tres b/BigFont.tres new file mode 100644 index 00000000..2360313e --- /dev/null +++ b/BigFont.tres @@ -0,0 +1,8 @@ +[gd_resource type="DynamicFont" load_steps=2 format=2] + +[sub_resource type="DynamicFontData" id=9] +font_path = "res://addons/gut/fonts/LobsterTwo-BoldItalic.ttf" + +[resource] +size = 40 +font_data = SubResource( 9 ) diff --git a/BigFontTheme.tres b/BigFontTheme.tres new file mode 100644 index 00000000..8de62602 --- /dev/null +++ b/BigFontTheme.tres @@ -0,0 +1,6 @@ +[gd_resource type="Theme" load_steps=2 format=2] + +[ext_resource path="res://BigFont.tres" type="DynamicFont" id=1] + +[resource] +default_font = ExtResource( 1 ) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13472bf9..c8f7075a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ * `CardFilter` class can now call `custom_check()`. This allows any game to extend it with extra functionality * `CardFilter` class can now filter against array/dictionary size +* Added option to specify if a pile should have sorted popups + +### Tweaks + +* Removed unnecessary use of seed rng during visual rng +* Prevents info panels moving outside viewport +* SelectionWindow now uses SignalPropagator +* SelectWindow can select direct cards +* Selection Window will now properly turn cards face-up +* Made the rich text card front the default card front +* Changed the Cost/Power values to use icons and no-text. +* Preview Popups will now tween their position when changing it +* Details Panels can now multiple columns, so that they don't grow infinitely long, pushing the card out of the viewport. + +### Bugfixes + +* Avoids card becoming unplayable randomly. +* Avoids crash when Scripting Engine is used on non-card objects +* CardViewer now works with "scale" resizing properly #### ScriptingEngine @@ -13,6 +32,12 @@ Regardless of where the card is. * ScriptingEngine filters can now filter using CardFilter objects * Added KEY_FILTER_EACH_REVIOUS_SUBJECT to use in tasks utilizing KEY_SUBJECT_V_PREVIOUS. +* Added KEY_UP_TO to use with KEY_SUBJECT_V_TUTOR +* Added `_pre_task_exec()` in ScriptingEngine +* Alterant Engine can now take into account script subject +* Added signal when card scripts finish +* Fixed per_ in alterant engine not working +* Prevents crash when card container empty while looking for card index. ## 2.1 diff --git a/README.md b/README.md index 9d3f4298..88ca85f7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# Godot Card Game Framework [2.1](CHANGELOG.md) +# Godot Card Game Framework [2.2](CHANGELOG.md) [![GUT](https://github.com/db0/godot-card-game-framework/actions/workflows/main.yml/badge.svg)](https://github.com/db0/godot-card-game-framework/actions/workflows/main.yml) @@ -29,7 +29,7 @@ Pull requests are more than welcome ;) * Cards can target other cards with a draggable arrow * Can flip cards face-down and view them while in that state * Can add tokens on cards. Tokens expand in the own drawer for more info. -* Ability to define cards in standard json +* Ability to define cards in standard dictionaries * Ability to split card definitions into sets * Automatically resizing text inside cards to fit the card size. * Supports resizing the window, in all stretch modes (including disabled stretch) @@ -50,7 +50,7 @@ Pull requests are more than welcome ;) ### Scripting Engine Features -* Can define card scripts in plain text, using simple json. +* Can define card scripts in plain text, using dictionaries. * Can set cards to trigger off of any board manipulation. * Can filter the triggers based on card properties, or a special subset. * Can define optional abilities. @@ -61,7 +61,7 @@ Pull requests are more than welcome ;) * Can store results from one script to use in another. * Can be plugged into by any object, not just cards. -All of the above while being very easily extensible to your own game's special requirements through simple json dictionaries. +All of the above while being very easily extensible to your own game's special requirements through simple dictionaries. ## Easy Customization diff --git a/addons/gut/GutScene.gd b/addons/gut/GutScene.gd index 9894c9cf..251ab8f8 100644 --- a/addons/gut/GutScene.gd +++ b/addons/gut/GutScene.gd @@ -1,35 +1,46 @@ extends Panel onready var _script_list = $ScriptsList +onready var _nav_container = $VBox/BottomPanel/VBox/HBox/Navigation onready var _nav = { - prev = $Navigation/Previous, - next = $Navigation/Next, - run = $Navigation/Run, - current_script = $Navigation/CurrentScript, - run_single = $Navigation/RunSingleScript + container = _nav_container, + prev = _nav_container.get_node('VBox/HBox/Previous'), + next = _nav_container.get_node('VBox/HBox/Next'), + run = _nav_container.get_node('VBox/HBox/Run'), + current_script = _nav_container.get_node('VBox/CurrentScript'), + run_single = _nav_container.get_node('VBox/HBox/RunSingleScript') } + +onready var _progress_container = $VBox/BottomPanel/VBox/HBox/Progress onready var _progress = { - script = $ScriptProgress, - script_xy = $ScriptProgress/xy, - test = $TestProgress, - test_xy = $TestProgress/xy + script = _progress_container.get_node("ScriptProgress"), + script_xy = _progress_container.get_node("ScriptProgress/xy"), + test = _progress_container.get_node("TestProgress"), + test_xy = _progress_container.get_node("TestProgress/xy") } onready var _summary = { - failing = $Summary/Failing, - passing = $Summary/Passing, + control = $VBox/TitleBar/HBox/Summary, + failing = $VBox/TitleBar/HBox/Summary/Failing, + passing = $VBox/TitleBar/HBox/Summary/Passing, + asserts = $VBox/TitleBar/HBox/Summary/AssertCount, fail_count = 0, pass_count = 0 } onready var _extras = $ExtraOptions onready var _ignore_pauses = $ExtraOptions/IgnorePause -onready var _continue_button = $Continue/Continue -onready var _text_box = $TextDisplay/RichTextLabel +onready var _continue_button = $VBox/BottomPanel/VBox/HBox/Continue/Continue +onready var _text_box = $VBox/TextDisplay/RichTextLabel +onready var _text_box_container = $VBox/TextDisplay +onready var _log_level_slider = $VBox/BottomPanel/VBox/HBox2/LogLevelSlider +onready var _resize_handle = $ResizeHandle +onready var _current_script = $VBox/BottomPanel/VBox/HBox2/CurrentScriptLabel +onready var _title_replacement = $VBox/TitleBar/HBox/TitleReplacement onready var _titlebar = { - bar = $TitleBar, - time = $TitleBar/Time, - label = $TitleBar/Title + bar = $VBox/TitleBar, + time = $VBox/TitleBar/HBox/Time, + label = $VBox/TitleBar/HBox/Title } onready var _user_files = $UserFileViewer @@ -40,13 +51,20 @@ var _mouse = { down_pos = null, in_handle = false } + var _is_running = false var _start_time = 0.0 var _time = 0.0 -const DEFAULT_TITLE = 'Gut: The Godot Unit Testing tool.' +const DEFAULT_TITLE = 'GUT' var _pre_maximize_rect = null var _font_size = 20 +var _compact_mode = false + +var min_sizes = { + compact = Vector2(330, 100), + full = Vector2(740, 300), +} signal end_pause signal ignore_pause @@ -55,17 +73,17 @@ signal run_script signal run_single_script func _ready(): - if(Engine.editor_hint): return + _current_script.text = '' _pre_maximize_rect = get_rect() _hide_scripts() _update_controls() _nav.current_script.set_text("No scripts available") set_title() clear_summary() - _titlebar.time.set_text("Time 0.0") + _titlebar.time.set_text("t: 0.0") _extras.visible = false update() @@ -81,7 +99,7 @@ func elapsed_time_as_str(): func _process(_delta): if(_is_running): _time = OS.get_ticks_msec() - _start_time - _titlebar.time.set_text(str('Time: ', elapsed_time_as_str())) + _titlebar.time.set_text(str('t: ', elapsed_time_as_str())) func _draw(): # needs get_size() # Draw the lines in the corner to show where you can @@ -89,22 +107,23 @@ func _draw(): # needs get_size() var grab_margin = 3 var line_space = 3 var grab_line_color = Color(.4, .4, .4) - for i in range(1, 10): - var x = rect_size - Vector2(i * line_space, grab_margin) - var y = rect_size - Vector2(grab_margin, i * line_space) - draw_line(x, y, grab_line_color, 1, true) + if(_resize_handle.visible): + for i in range(1, 10): + var x = rect_size - Vector2(i * line_space, grab_margin) + var y = rect_size - Vector2(grab_margin, i * line_space) + draw_line(x, y, grab_line_color, 1, true) func _on_Maximize_draw(): # draw the maximize square thing. - var btn = $TitleBar/Maximize + var btn = $VBox/TitleBar/HBox/Maximize btn.set_text('') var w = btn.get_size().x var h = btn.get_size().y - btn.draw_rect(Rect2(0, 0, w, h), Color(0, 0, 0, 1)) - btn.draw_rect(Rect2(2, 4, w - 4, h - 6), Color(1,1,1,1)) + btn.draw_rect(Rect2(0, 2, w, h -2), Color(0, 0, 0, 1)) + btn.draw_rect(Rect2(2, 6, w - 4, h - 8), Color(1,1,1,1)) func _on_ShowExtras_draw(): - var btn = $Continue/ShowExtras + var btn = $VBox/BottomPanel/VBox/HBox/Continue/ShowExtras btn.set_text('') var start_x = 20 var start_y = 15 @@ -132,7 +151,7 @@ func _on_Next_pressed(): _select_script(get_selected_index() + 1) func _on_LogLevelSlider_value_changed(_value): - emit_signal('log_level_changed', $LogLevelSlider.value) + emit_signal('log_level_changed', _log_level_slider.value) func _on_Continue_pressed(): _continue_button.disabled = true @@ -206,10 +225,29 @@ func _on_ShowExtras_toggled(button_pressed): func _on_Maximize_pressed(): if(get_rect() == _pre_maximize_rect): + compact_mode(false) maximize() else: + compact_mode(false) rect_size = _pre_maximize_rect.size rect_position = _pre_maximize_rect.position +func _on_Minimize_pressed(): + + compact_mode(!_compact_mode) + + +func _on_Minimize_draw(): + # draw the maximize square thing. + var btn = $VBox/TitleBar/HBox/Minimize + btn.set_text('') + var w = btn.get_size().x + var h = btn.get_size().y + btn.draw_rect(Rect2(0, h-3, w, 3), Color(0, 0, 0, 1)) + +func _on_UserFiles_pressed(): + _user_files.show_open() + + # #################### # Private # #################### @@ -221,16 +259,18 @@ func _run_mode(is_running=true): _is_running = is_running _hide_scripts() - var ctrls = $Navigation.get_children() - for i in range(ctrls.size()): - ctrls[i].disabled = is_running + _nav.prev.disabled = is_running + _nav.next.disabled = is_running + _nav.run.disabled = is_running + _nav.current_script.disabled = is_running + _nav.run_single.disabled = is_running func _select_script(index): var text = _script_list.get_item_text(index) var max_len = 50 if(text.length() > max_len): text = '...' + text.right(text.length() - (max_len - 5)) - $Navigation/CurrentScript.set_text(text) + _nav.current_script.set_text(text) _script_list.select(index) _update_controls() @@ -265,8 +305,8 @@ func _update_summary(): return var total = _summary.fail_count + _summary.pass_count - $Summary.visible = !total == 0 - $Summary/AssertCount.text = str('Failures ', _summary.fail_count, '/', total) + _summary.control.visible = !total == 0 + _summary.asserts.text = str('Failures ', _summary.fail_count, '/', total) # #################### # Public # #################### @@ -287,13 +327,15 @@ func get_selected_index(): return _script_list.get_selected_items()[0] func get_log_level(): - return $LogLevelSlider.value + return _log_level_slider.value func set_log_level(value): var new_value = value if(new_value == null): new_value = 0 - $LogLevelSlider.value = new_value + # !! For some reason, _log_level_slider was null, but this wasn't, so + # here's another hardcoded node path. + $VBox/BottomPanel/VBox/HBox2/LogLevelSlider.value = new_value func set_ignore_pause(should): _ignore_pauses.pressed = should @@ -304,7 +346,7 @@ func get_ignore_pause(): func get_text_box(): # due to some timing issue, this cannot return _text_box but can return # this. - return $TextDisplay/RichTextLabel + return $VBox/TextDisplay/RichTextLabel func end_run(): _run_mode(false) @@ -339,9 +381,9 @@ func pause(): func set_title(title=null): if(title == null): - $TitleBar/Title.set_text(DEFAULT_TITLE) + _titlebar.label.set_text(DEFAULT_TITLE) else: - $TitleBar/Title.set_text(title) + _titlebar.label.set_text(title) func add_passing(amount=1): if(!_summary): @@ -362,7 +404,7 @@ func clear_summary(): func maximize(): if(is_inside_tree()): - var vp_size_offset = get_viewport().size + var vp_size_offset = get_tree().root.get_viewport().get_visible_rect().size rect_size = vp_size_offset / get_scale() set_position(Vector2(0, 0)) @@ -423,10 +465,38 @@ func set_default_font_color(color): _text_box.set('custom_colors/default_color', color) func set_background_color(color): - $TextDisplay.color = color - -func _on_UserFiles_pressed(): - _user_files.show_open() + _text_box_container.color = color func get_waiting_label(): - return $TextDisplay/WaitingLabel + return $VBox/TextDisplay/WaitingLabel + +func compact_mode(should): + if(_compact_mode == should): + return + + _compact_mode = should + _text_box_container.visible = !should + _nav.container.visible = !should + _log_level_slider.visible = !should + $VBox/BottomPanel/VBox/HBox/Continue/ShowExtras.visible = !should + _titlebar.label.visible = !should + _resize_handle.visible = !should + _current_script.visible = !should + _title_replacement.visible = should + + if(should): + rect_min_size = min_sizes.compact + rect_size = rect_min_size + else: + rect_min_size = min_sizes.full + rect_size = min_sizes.full + + goto_bottom_right_corner() + + +func set_script_path(text): + _current_script.text = text + + +func goto_bottom_right_corner(): + rect_position = get_tree().root.get_viewport().get_visible_rect().size - rect_size diff --git a/addons/gut/GutScene.tscn b/addons/gut/GutScene.tscn index 3819a6e6..a2ea3b08 100644 --- a/addons/gut/GutScene.tscn +++ b/addons/gut/GutScene.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=15 format=2] +[gd_scene load_steps=16 format=2] [ext_resource path="res://addons/gut/GutScene.gd" type="Script" id=1] [ext_resource path="res://addons/gut/fonts/AnonymousPro-Italic.ttf" type="DynamicFontData" id=2] @@ -6,6 +6,7 @@ [ext_resource path="res://addons/gut/fonts/AnonymousPro-BoldItalic.ttf" type="DynamicFontData" id=4] [ext_resource path="res://addons/gut/fonts/AnonymousPro-Bold.ttf" type="DynamicFontData" id=5] [ext_resource path="res://addons/gut/UserFileViewer.tscn" type="PackedScene" id=6] +[ext_resource path="res://addons/gut/gui/GutSceneTheme.tres" type="Theme" id=7] [sub_resource type="StyleBoxFlat" id=1] bg_color = Color( 0.192157, 0.192157, 0.227451, 1 ) @@ -42,9 +43,10 @@ corner_radius_top_left = 20 corner_radius_top_right = 20 [node name="Gut" type="Panel"] -margin_right = 880.0 -margin_bottom = 360.0 -rect_min_size = Vector2( 740, 250 ) +margin_right = 740.0 +margin_bottom = 300.0 +rect_min_size = Vector2( 740, 300 ) +theme = ExtResource( 7 ) custom_styles/panel = SubResource( 1 ) script = ExtResource( 1 ) __meta__ = { @@ -55,132 +57,168 @@ __meta__ = { margin_top = 388.0 margin_bottom = 818.0 -[node name="TitleBar" type="Panel" parent="."] -anchor_top = -0.000491047 +[node name="VBox" type="VBoxContainer" parent="."] anchor_right = 1.0 -anchor_bottom = -0.000491047 -margin_left = 1.0 -margin_top = 1.17678 -margin_right = -1.0 -margin_bottom = 40.1768 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TitleBar" type="Panel" parent="VBox"] +margin_right = 740.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 0, 30 ) theme = SubResource( 3 ) __meta__ = { "_edit_group_": true, "_edit_use_anchors_": false } -[node name="Title" type="Label" parent="TitleBar"] +[node name="HBox" type="HBoxContainer" parent="VBox/TitleBar"] anchor_right = 1.0 -margin_bottom = 40.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "Gut" -align = 1 -valign = 1 - -[node name="Time" type="Label" parent="TitleBar"] -anchor_left = 1.0 -anchor_right = 1.0 -margin_left = -105.0 -margin_right = -53.0 -margin_bottom = 40.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "9999.99" -valign = 1 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} -[node name="Maximize" type="Button" parent="TitleBar"] -anchor_left = 1.0 -anchor_right = 1.0 -margin_left = -30.0 -margin_top = 10.0 -margin_right = -6.0 +[node name="Summary" type="Control" parent="VBox/TitleBar/HBox"] +margin_right = 110.0 margin_bottom = 30.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "M" -flat = true +rect_min_size = Vector2( 110, 0 ) +mouse_filter = 2 +__meta__ = { +"_edit_use_anchors_": false +} -[node name="ScriptProgress" type="ProgressBar" parent="."] -anchor_top = 1.0 -anchor_bottom = 1.0 -margin_left = 75.0 -margin_top = -70.0 -margin_right = 185.0 -margin_bottom = -40.0 -hint_tooltip = "Overall progress of executing tests." -step = 1.0 +[node name="Passing" type="Label" parent="VBox/TitleBar/HBox/Summary"] +visible = false +margin_left = 5.0 +margin_top = 7.0 +margin_right = 45.0 +margin_bottom = 21.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "0" +align = 1 +valign = 1 __meta__ = { "_edit_use_anchors_": false } -[node name="Label" type="Label" parent="ScriptProgress"] -margin_left = -70.0 -margin_right = -5.0 -margin_bottom = 30.0 -text = "Scripts" +[node name="Failing" type="Label" parent="VBox/TitleBar/HBox/Summary"] +visible = false +margin_left = 100.0 +margin_top = 7.0 +margin_right = 140.0 +margin_bottom = 21.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "0" +align = 1 valign = 1 + +[node name="AssertCount" type="Label" parent="VBox/TitleBar/HBox/Summary"] +margin_left = 5.0 +margin_top = 7.0 +margin_right = 165.0 +margin_bottom = 21.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "Assert count" __meta__ = { "_edit_use_anchors_": false } -[node name="xy" type="Label" parent="ScriptProgress"] +[node name="TitleReplacement" type="CenterContainer" parent="VBox/TitleBar/HBox"] visible = false -margin_right = 110.0 +margin_left = 114.0 +margin_right = 352.0 margin_bottom = 30.0 -text = "0/0" +rect_min_size = Vector2( 5, 0 ) +mouse_filter = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Title" type="Label" parent="VBox/TitleBar/HBox"] +margin_left = 114.0 +margin_right = 594.0 +margin_bottom = 30.0 +size_flags_horizontal = 3 +size_flags_vertical = 7 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "Gut" align = 1 valign = 1 __meta__ = { "_edit_use_anchors_": false } -[node name="TestProgress" type="ProgressBar" parent="."] -anchor_top = 1.0 -anchor_bottom = 1.0 -margin_left = 75.0 -margin_top = -105.0 -margin_right = 185.0 -margin_bottom = -75.0 -hint_tooltip = "Test progress for the current script." -step = 1.0 +[node name="Time" type="Label" parent="VBox/TitleBar/HBox"] +margin_left = 598.0 +margin_top = 7.0 +margin_right = 654.0 +margin_bottom = 22.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "9999.99" +valign = 1 __meta__ = { "_edit_use_anchors_": false } -[node name="Label" type="Label" parent="TestProgress"] -margin_left = -70.0 -margin_right = -5.0 +[node name="CC" type="CenterContainer" parent="VBox/TitleBar/HBox"] +margin_left = 658.0 +margin_right = 663.0 margin_bottom = 30.0 -text = "Tests" -valign = 1 +rect_min_size = Vector2( 5, 0 ) +mouse_filter = 2 + +[node name="Minimize" type="Button" parent="VBox/TitleBar/HBox"] +margin_left = 667.0 +margin_right = 697.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 30, 0 ) +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "N" +flat = true __meta__ = { "_edit_use_anchors_": false } -[node name="xy" type="Label" parent="TestProgress"] -visible = false -margin_right = 110.0 +[node name="Maximize" type="Button" parent="VBox/TitleBar/HBox"] +margin_left = 701.0 +margin_right = 731.0 margin_bottom = 30.0 -text = "0/0" -align = 1 -valign = 1 +rect_min_size = Vector2( 30, 0 ) +custom_colors/font_color = Color( 0, 0, 0, 1 ) +text = "X" +flat = true __meta__ = { "_edit_use_anchors_": false } -[node name="TextDisplay" type="ColorRect" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_top = 40.0 -margin_bottom = -110.0 +[node name="CC2" type="CenterContainer" parent="VBox/TitleBar/HBox"] +margin_left = 735.0 +margin_right = 740.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 5, 0 ) +mouse_filter = 2 + +[node name="TextDisplay" type="ColorRect" parent="VBox"] +margin_top = 34.0 +margin_right = 740.0 +margin_bottom = 176.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 color = Color( 0, 0, 0, 1 ) __meta__ = { "_edit_use_anchors_": false } -[node name="RichTextLabel" type="RichTextLabel" parent="TextDisplay"] +[node name="RichTextLabel" type="RichTextLabel" parent="VBox/TextDisplay"] anchor_right = 1.0 anchor_bottom = 1.0 margin_left = 10.0 +rect_min_size = Vector2( 0, 116 ) focus_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 custom_fonts/bold_italics_font = SubResource( 4 ) custom_fonts/italics_font = SubResource( 5 ) custom_fonts/bold_font = SubResource( 6 ) @@ -192,7 +230,7 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="WaitingLabel" type="RichTextLabel" parent="TextDisplay"] +[node name="WaitingLabel" type="RichTextLabel" parent="VBox/TextDisplay"] anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 @@ -202,89 +240,269 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="Navigation" type="Panel" parent="."] -self_modulate = Color( 1, 1, 1, 0 ) -anchor_top = 1.0 +[node name="BottomPanel" type="ColorRect" parent="VBox"] +margin_top = 180.0 +margin_right = 740.0 +margin_bottom = 300.0 +rect_min_size = Vector2( 0, 120 ) +size_flags_horizontal = 9 +size_flags_vertical = 9 +color = Color( 1, 1, 1, 0 ) + +[node name="VBox" type="VBoxContainer" parent="VBox/BottomPanel"] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HBox" type="HBoxContainer" parent="VBox/BottomPanel/VBox"] +margin_right = 740.0 +margin_bottom = 80.0 +size_flags_horizontal = 3 + +[node name="CC1" type="CenterContainer" parent="VBox/BottomPanel/VBox/HBox"] +margin_right = 5.0 +margin_bottom = 80.0 +rect_min_size = Vector2( 5, 0 ) + +[node name="Progress" type="VBoxContainer" parent="VBox/BottomPanel/VBox/HBox"] +margin_left = 9.0 +margin_right = 179.0 +margin_bottom = 80.0 +rect_min_size = Vector2( 170, 0 ) +alignment = 1 + +[node name="TestProgress" type="ProgressBar" parent="VBox/BottomPanel/VBox/HBox/Progress"] +margin_top = 11.0 +margin_right = 100.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 100, 25 ) +hint_tooltip = "Test progress for the current script." +size_flags_horizontal = 0 +step = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="VBox/BottomPanel/VBox/HBox/Progress/TestProgress"] +margin_left = 107.5 +margin_top = 3.0 +margin_right = 172.5 +margin_bottom = 18.0 +text = "Tests" +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="xy" type="Label" parent="VBox/BottomPanel/VBox/HBox/Progress/TestProgress"] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +text = "0/0" +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ScriptProgress" type="ProgressBar" parent="VBox/BottomPanel/VBox/HBox/Progress"] +margin_top = 40.0 +margin_right = 100.0 +margin_bottom = 65.0 +rect_min_size = Vector2( 100, 25 ) +hint_tooltip = "Overall progress of executing tests." +size_flags_horizontal = 0 +step = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="VBox/BottomPanel/VBox/HBox/Progress/ScriptProgress"] +margin_left = 107.0 +margin_top = 3.5 +margin_right = 172.0 +margin_bottom = 18.5 +text = "Scripts" +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="xy" type="Label" parent="VBox/BottomPanel/VBox/HBox/Progress/ScriptProgress"] +visible = false +anchor_right = 1.0 anchor_bottom = 1.0 -margin_left = 220.0 -margin_top = -99.0 +text = "0/0" +align = 1 +valign = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CenterContainer" type="CenterContainer" parent="VBox/BottomPanel/VBox/HBox/Progress"] +margin_top = 69.0 +margin_right = 170.0 +margin_bottom = 69.0 + +[node name="CC2" type="CenterContainer" parent="VBox/BottomPanel/VBox/HBox"] +margin_left = 183.0 +margin_right = 226.0 +margin_bottom = 80.0 +rect_min_size = Vector2( 5, 0 ) +size_flags_horizontal = 3 + +[node name="Navigation" type="Panel" parent="VBox/BottomPanel/VBox/HBox"] +self_modulate = Color( 1, 1, 1, 0 ) +margin_left = 230.0 margin_right = 580.0 -margin_bottom = 1.0 +margin_bottom = 80.0 +rect_min_size = Vector2( 350, 80 ) +__meta__ = { +"_edit_group_": true, +"_edit_use_anchors_": false +} + +[node name="VBox" type="VBoxContainer" parent="VBox/BottomPanel/VBox/HBox/Navigation"] +anchor_right = 1.0 +anchor_bottom = 1.0 __meta__ = { "_edit_use_anchors_": false } -[node name="Previous" type="Button" parent="Navigation"] -margin_left = -20.0 -margin_top = 44.0 -margin_right = 65.0 -margin_bottom = 84.0 +[node name="CurrentScript" type="Button" parent="VBox/BottomPanel/VBox/HBox/Navigation/VBox"] +margin_right = 350.0 +margin_bottom = 38.0 +hint_tooltip = "Select a script to run. You can run just this script, or this script and all scripts after using the run buttons." +size_flags_horizontal = 3 +size_flags_vertical = 3 +text = "res://test/unit/test_gut.gd" +clip_text = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HBox" type="HBoxContainer" parent="VBox/BottomPanel/VBox/HBox/Navigation/VBox"] +margin_top = 42.0 +margin_right = 350.0 +margin_bottom = 80.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Previous" type="Button" parent="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox"] +margin_right = 84.0 +margin_bottom = 38.0 hint_tooltip = "Previous script in the list." +size_flags_horizontal = 3 +size_flags_vertical = 3 text = "|<" __meta__ = { "_edit_use_anchors_": false } -[node name="Next" type="Button" parent="Navigation"] -margin_left = 250.0 -margin_top = 44.0 -margin_right = 335.0 -margin_bottom = 84.0 +[node name="Next" type="Button" parent="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox"] +margin_left = 88.0 +margin_right = 173.0 +margin_bottom = 38.0 hint_tooltip = "Next script in the list. " +size_flags_horizontal = 3 +size_flags_vertical = 3 text = ">|" __meta__ = { "_edit_use_anchors_": false } -[node name="Run" type="Button" parent="Navigation"] -margin_left = 70.0 -margin_top = 44.0 -margin_right = 155.0 -margin_bottom = 84.0 +[node name="Run" type="Button" parent="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox"] +margin_left = 177.0 +margin_right = 261.0 +margin_bottom = 38.0 hint_tooltip = "Run the currently selected item and all after it." +size_flags_horizontal = 3 +size_flags_vertical = 3 text = ">" __meta__ = { "_edit_use_anchors_": false } -[node name="CurrentScript" type="Button" parent="Navigation"] -anchor_top = -0.01 -anchor_bottom = -0.01 -margin_left = -20.0 -margin_top = -5.0 -margin_right = 335.0 -margin_bottom = 35.0 -hint_tooltip = "Select a script to run. You can run just this script, or this script and all scripts after using the run buttons." -text = "res://test/unit/test_gut.gd" -clip_text = true -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="RunSingleScript" type="Button" parent="Navigation"] -margin_left = 160.0 -margin_top = 44.0 -margin_right = 245.0 -margin_bottom = 84.0 +[node name="RunSingleScript" type="Button" parent="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox"] +margin_left = 265.0 +margin_right = 350.0 +margin_bottom = 38.0 hint_tooltip = "Run the currently selected item. If the selected item has Inner Test Classes then they will all be run. If the selected item is an Inner Test Class then only it will be run." +size_flags_horizontal = 3 +size_flags_vertical = 3 text = "> (1)" __meta__ = { "_edit_use_anchors_": false } -[node name="LogLevelSlider" type="HSlider" parent="."] -anchor_top = 1.0 -anchor_bottom = 1.0 -margin_left = 80.0 -margin_top = -40.0 -margin_right = 130.0 -margin_bottom = -20.0 -rect_scale = Vector2( 2, 2 ) +[node name="CC3" type="CenterContainer" parent="VBox/BottomPanel/VBox/HBox"] +margin_left = 584.0 +margin_right = 627.0 +margin_bottom = 80.0 +rect_min_size = Vector2( 5, 0 ) +size_flags_horizontal = 3 + +[node name="Continue" type="VBoxContainer" parent="VBox/BottomPanel/VBox/HBox"] +self_modulate = Color( 1, 1, 1, 0 ) +margin_left = 631.0 +margin_right = 731.0 +margin_bottom = 80.0 +alignment = 1 + +[node name="ShowExtras" type="Button" parent="VBox/BottomPanel/VBox/HBox/Continue"] +margin_right = 50.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 50, 35 ) +rect_pivot_offset = Vector2( 35, 20 ) +hint_tooltip = "Show/hide additional options." +size_flags_horizontal = 0 +toggle_mode = true +text = "_" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Continue" type="Button" parent="VBox/BottomPanel/VBox/HBox/Continue"] +margin_top = 39.0 +margin_right = 100.0 +margin_bottom = 79.0 +rect_min_size = Vector2( 100, 40 ) +hint_tooltip = "When a pause_before_teardown is encountered this button will be enabled and must be pressed to continue running tests." +disabled = true +text = "Continue" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CC4" type="CenterContainer" parent="VBox/BottomPanel/VBox/HBox"] +margin_left = 735.0 +margin_right = 740.0 +margin_bottom = 80.0 +rect_min_size = Vector2( 5, 0 ) + +[node name="HBox2" type="HBoxContainer" parent="VBox/BottomPanel/VBox"] +margin_top = 84.0 +margin_right = 740.0 +margin_bottom = 114.0 + +[node name="CC" type="CenterContainer" parent="VBox/BottomPanel/VBox/HBox2"] +margin_right = 5.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 5, 0 ) + +[node name="LogLevelSlider" type="HSlider" parent="VBox/BottomPanel/VBox/HBox2"] +margin_left = 9.0 +margin_right = 109.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 100, 30 ) +size_flags_vertical = 3 max_value = 2.0 tick_count = 3 ticks_on_borders = true @@ -292,18 +510,37 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="Label" type="Label" parent="LogLevelSlider"] -margin_left = -37.0 -margin_right = 28.0 -margin_bottom = 40.0 -rect_scale = Vector2( 0.5, 0.5 ) +[node name="Label" type="Label" parent="VBox/BottomPanel/VBox/HBox2/LogLevelSlider"] +margin_left = 4.0 +margin_top = -17.0 +margin_right = 85.0 +margin_bottom = 7.0 text = "Log Level" valign = 1 __meta__ = { "_edit_use_anchors_": false } +[node name="CenterContainer" type="CenterContainer" parent="VBox/BottomPanel/VBox/HBox2"] +margin_left = 113.0 +margin_right = 163.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 50, 0 ) + +[node name="CurrentScriptLabel" type="Label" parent="VBox/BottomPanel/VBox/HBox2"] +margin_left = 167.0 +margin_top = 7.0 +margin_right = 740.0 +margin_bottom = 22.0 +size_flags_horizontal = 3 +size_flags_vertical = 6 +text = "res://test/unit/test_something.gd" +__meta__ = { +"_edit_use_anchors_": false +} + [node name="ScriptsList" type="ItemList" parent="."] +visible = false anchor_bottom = 1.0 margin_left = 179.0 margin_top = 40.0 @@ -319,6 +556,7 @@ wait_time = 0.3 one_shot = true [node name="ExtraOptions" type="Panel" parent="."] +visible = false anchor_left = 1.0 anchor_top = 1.0 anchor_right = 1.0 @@ -333,10 +571,11 @@ __meta__ = { } [node name="IgnorePause" type="CheckBox" parent="ExtraOptions"] -margin_left = 18.0 -margin_right = 136.0 -margin_bottom = 24.0 -rect_scale = Vector2( 1.5, 1.5 ) +margin_left = 17.5 +margin_top = 4.5 +margin_right = 162.5 +margin_bottom = 29.5 +rect_scale = Vector2( 1.2, 1.2 ) hint_tooltip = "Ignore all calls to pause_before_teardown." text = "Ignore Pauses" __meta__ = { @@ -372,100 +611,29 @@ anchor_right = 1.0 anchor_bottom = 1.0 margin_left = -40.0 margin_top = -40.0 - -[node name="Continue" type="Panel" parent="."] -self_modulate = Color( 1, 1, 1, 0 ) -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = -150.0 -margin_top = -100.0 -margin_right = -30.0 -margin_bottom = -10.0 - -[node name="Continue" type="Button" parent="Continue"] -margin_left = -2.0 -margin_top = 45.0 -margin_right = 117.0 -margin_bottom = 85.0 -hint_tooltip = "When a pause_before_teardown is encountered this button will be enabled and must be pressed to continue running tests." -disabled = true -text = "Continue" -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="ShowExtras" type="Button" parent="Continue"] -anchor_left = -0.0166667 -anchor_right = -0.0166667 -margin_left = 50.0 -margin_top = -5.0 -margin_right = 120.0 -margin_bottom = 35.0 -rect_pivot_offset = Vector2( 35, 20 ) -hint_tooltip = "Show/hide additional options." -toggle_mode = true -text = "_" __meta__ = { "_edit_use_anchors_": false } -[node name="Summary" type="Node2D" parent="."] -position = Vector2( 0, 3 ) - -[node name="Passing" type="Label" parent="Summary"] -visible = false -margin_left = 5.0 -margin_top = 7.0 -margin_right = 45.0 -margin_bottom = 21.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "0" -align = 1 -valign = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Failing" type="Label" parent="Summary"] -visible = false -margin_left = 100.0 -margin_top = 7.0 -margin_right = 140.0 -margin_bottom = 21.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "0" -align = 1 -valign = 1 - -[node name="AssertCount" type="Label" parent="Summary"] -margin_left = 5.0 -margin_top = 7.0 -margin_right = 165.0 -margin_bottom = 21.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "Assert count" -__meta__ = { -"_edit_use_anchors_": false -} -[connection signal="mouse_entered" from="TitleBar" to="." method="_on_TitleBar_mouse_entered"] -[connection signal="mouse_exited" from="TitleBar" to="." method="_on_TitleBar_mouse_exited"] -[connection signal="draw" from="TitleBar/Maximize" to="." method="_on_Maximize_draw"] -[connection signal="pressed" from="TitleBar/Maximize" to="." method="_on_Maximize_pressed"] -[connection signal="gui_input" from="TextDisplay/RichTextLabel" to="." method="_on_RichTextLabel_gui_input"] -[connection signal="pressed" from="Navigation/Previous" to="." method="_on_Previous_pressed"] -[connection signal="pressed" from="Navigation/Next" to="." method="_on_Next_pressed"] -[connection signal="pressed" from="Navigation/Run" to="." method="_on_Run_pressed"] -[connection signal="pressed" from="Navigation/CurrentScript" to="." method="_on_CurrentScript_pressed"] -[connection signal="pressed" from="Navigation/RunSingleScript" to="." method="_on_RunSingleScript_pressed"] -[connection signal="value_changed" from="LogLevelSlider" to="." method="_on_LogLevelSlider_value_changed"] +[connection signal="mouse_entered" from="VBox/TitleBar" to="." method="_on_TitleBar_mouse_entered"] +[connection signal="mouse_exited" from="VBox/TitleBar" to="." method="_on_TitleBar_mouse_exited"] +[connection signal="draw" from="VBox/TitleBar/HBox/Minimize" to="." method="_on_Minimize_draw"] +[connection signal="pressed" from="VBox/TitleBar/HBox/Minimize" to="." method="_on_Minimize_pressed"] +[connection signal="draw" from="VBox/TitleBar/HBox/Maximize" to="." method="_on_Maximize_draw"] +[connection signal="pressed" from="VBox/TitleBar/HBox/Maximize" to="." method="_on_Maximize_pressed"] +[connection signal="gui_input" from="VBox/TextDisplay/RichTextLabel" to="." method="_on_RichTextLabel_gui_input"] +[connection signal="pressed" from="VBox/BottomPanel/VBox/HBox/Navigation/VBox/CurrentScript" to="." method="_on_CurrentScript_pressed"] +[connection signal="pressed" from="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox/Previous" to="." method="_on_Previous_pressed"] +[connection signal="pressed" from="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox/Next" to="." method="_on_Next_pressed"] +[connection signal="pressed" from="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox/Run" to="." method="_on_Run_pressed"] +[connection signal="pressed" from="VBox/BottomPanel/VBox/HBox/Navigation/VBox/HBox/RunSingleScript" to="." method="_on_RunSingleScript_pressed"] +[connection signal="draw" from="VBox/BottomPanel/VBox/HBox/Continue/ShowExtras" to="." method="_on_ShowExtras_draw"] +[connection signal="toggled" from="VBox/BottomPanel/VBox/HBox/Continue/ShowExtras" to="." method="_on_ShowExtras_toggled"] +[connection signal="pressed" from="VBox/BottomPanel/VBox/HBox/Continue/Continue" to="." method="_on_Continue_pressed"] +[connection signal="value_changed" from="VBox/BottomPanel/VBox/HBox2/LogLevelSlider" to="." method="_on_LogLevelSlider_value_changed"] [connection signal="item_selected" from="ScriptsList" to="." method="_on_ScriptsList_item_selected"] [connection signal="pressed" from="ExtraOptions/IgnorePause" to="." method="_on_IgnorePause_pressed"] [connection signal="pressed" from="ExtraOptions/Copy" to="." method="_on_Copy_pressed"] [connection signal="pressed" from="ExtraOptions/UserFiles" to="." method="_on_UserFiles_pressed"] [connection signal="mouse_entered" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_entered"] [connection signal="mouse_exited" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_exited"] -[connection signal="pressed" from="Continue/Continue" to="." method="_on_Continue_pressed"] -[connection signal="draw" from="Continue/ShowExtras" to="." method="_on_ShowExtras_draw"] -[connection signal="toggled" from="Continue/ShowExtras" to="." method="_on_ShowExtras_toggled"] diff --git a/addons/gut/UserFileViewer.tscn b/addons/gut/UserFileViewer.tscn index 1236ebbe..528d1cdd 100644 --- a/addons/gut/UserFileViewer.tscn +++ b/addons/gut/UserFileViewer.tscn @@ -118,6 +118,7 @@ margin_right = 80.0 margin_bottom = -30.0 rect_scale = Vector2( 2, 2 ) text = "Close" + [connection signal="file_selected" from="FileDialog" to="." method="_on_FileDialog_file_selected"] [connection signal="popup_hide" from="FileDialog" to="." method="_on_FileDialog_popup_hide"] [connection signal="pressed" from="OpenFile" to="." method="_on_OpenFile_pressed"] diff --git a/addons/gut/diff_tool.gd b/addons/gut/diff_tool.gd index 9dbbd1cb..daef7ed5 100644 --- a/addons/gut/diff_tool.gd +++ b/addons/gut/diff_tool.gd @@ -159,4 +159,4 @@ func get_value_1(): func get_value_2(): - return _value_2 + return _value_2 \ No newline at end of file diff --git a/addons/gut/double_templates/script_template.txt b/addons/gut/double_templates/script_template.txt index 6fc71654..b1e1d771 100644 --- a/addons/gut/double_templates/script_template.txt +++ b/addons/gut/double_templates/script_template.txt @@ -1,11 +1,22 @@ +# ############################################################################## +# Start Script +# ############################################################################## {extends} +{constants} + +{properties} +# ------------------------------------------------------------------------------ +# GUT Double properties and methods +# ------------------------------------------------------------------------------ var __gut_metadata_ = { path = '{path}', subpath = '{subpath}', stubber = __gut_instance_from_id({stubber_id}), spy = __gut_instance_from_id({spy_id}), gut = __gut_instance_from_id({gut_id}), + from_singleton = '{singleton_name}', + is_partial = {is_partial} } func __gut_instance_from_id(inst_id): @@ -32,6 +43,13 @@ func __gut_get_stubbed_return(method_name, called_with): else: return null +func __gut_default_val(method_name, p_index): + if(__gut_metadata_.stubber != null): + return __gut_metadata_.stubber.get_default_value(self, method_name, p_index) + else: + return null + + func _init(): if(__gut_metadata_.gut != null): __gut_metadata_.gut.get_autofree().add_free(self) diff --git a/addons/gut/doubler.gd b/addons/gut/doubler.gd index c5e9e0ed..78becdde 100644 --- a/addons/gut/doubler.gd +++ b/addons/gut/doubler.gd @@ -1,3 +1,35 @@ +# ############################################################################## +#(G)odot (U)nit (T)est class +# +# ############################################################################## +# The MIT License (MIT) +# ===================== +# +# Copyright (c) 2020 Tom "Butch" Wesley +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################## +# Description +# ----------- +# ############################################################################## + # ------------------------------------------------------------------------------ # Utility class to hold the local and built in methods separately. Add all local # methods FIRST, then add built ins. @@ -78,26 +110,34 @@ class ObjectInfo: var _path = null var _subpaths = [] var _utils = load('res://addons/gut/utils.gd').get_instance() + var _lgr = _utils.get_logger() var _method_strategy = null var make_partial_double = false var scene_path = null var _native_class = null var _native_class_name = null + var _singleton_instance = null + var _singleton_name = null func _init(path, subpath=null): _path = path if(subpath != null): - _subpaths = _utils.split_string(subpath, '/') + _subpaths = Array(subpath.split('/')) # Returns an instance of the class/inner class func instantiate(): var to_return = null - if(is_native()): + + if(_singleton_instance != null): + to_return = _singleton_instance + elif(is_native()): to_return = _native_class.new() else: to_return = get_loaded_class().new() + return to_return + # Can't call it get_class because that is reserved so it gets this ugly name. # Loads up the class and then any inner classes to give back a reference to # the desired Inner class (if there is any) @@ -107,42 +147,35 @@ class ObjectInfo: LoadedClass = LoadedClass.get(_subpaths[i]) return LoadedClass + func to_s(): return str(_path, '[', get_subpath(), ']') + func get_path(): return _path + func get_subpath(): - return _utils.join_array(_subpaths, '/') + return PoolStringArray(_subpaths).join('/') + func has_subpath(): return _subpaths.size() != 0 - func get_extends_text(): - var extend = null - if(is_native()): - var native = get_native_class_name() - if(native.begins_with('_')): - native = native.substr(1) - extend = str("extends ", native) - else: - extend = str("extends '", get_path(), "'") - - if(has_subpath()): - extend += str('.', get_subpath().replace('/', '.')) - - return extend func get_method_strategy(): return _method_strategy + func set_method_strategy(method_strategy): _method_strategy = method_strategy + func is_native(): return _native_class != null + func set_native_class(native_class): _native_class = native_class var inst = native_class.new() @@ -151,9 +184,98 @@ class ObjectInfo: if(!inst is Reference): inst.free() + func get_native_class_name(): return _native_class_name + + func get_singleton_instance(): + return _singleton_instance + + + func get_singleton_name(): + return _singleton_name + + + func set_singleton_name(singleton_name): + _singleton_name = singleton_name + _singleton_instance = _utils.get_singleton_by_name(_singleton_name) + + + func is_singleton(): + return _singleton_instance != null + + + func get_extends_text(): + var extend = null + if(is_singleton()): + extend = str("# Double of singleton ", _singleton_name, ", base class is Reference") + elif(is_native()): + var native = get_native_class_name() + if(native.begins_with('_')): + native = native.substr(1) + extend = str("extends ", native) + else: + extend = str("extends '", get_path(), "'") + + if(has_subpath()): + extend += str('.', get_subpath().replace('/', '.')) + + return extend + + + func get_constants_text(): + if(!is_singleton()): + return "" + + # do not include constants defined in the super class which for + # singletons stubs is Reference. + var exclude_constants = Array(ClassDB.class_get_integer_constant_list("Reference")) + var text = str("# -----\n# ", _singleton_name, " Constants\n# -----\n") + var constants = ClassDB.class_get_integer_constant_list(_singleton_name) + for c in constants: + if(!exclude_constants.has(c)): + var value = ClassDB.class_get_integer_constant(_singleton_name, c) + text += str("const ", c, " = ", value, "\n") + + return text + + func get_properties_text(): + if(!is_singleton()): + return "" + + var text = str("# -----\n# ", _singleton_name, " Properties\n# -----\n") + var props = ClassDB.class_get_property_list(_singleton_name) + for prop in props: + var accessors = {"setter":null, "getter":null} + var prop_text = str("var ", prop["name"]) + + var getter_name = "get_" + prop["name"] + if(ClassDB.class_has_method(_singleton_name, getter_name)): + accessors.getter = getter_name + else: + getter_name = "is_" + prop["name"] + if(ClassDB.class_has_method(_singleton_name, getter_name)): + accessors.getter = getter_name + + var setter_name = "set_" + prop["name"] + if(ClassDB.class_has_method(_singleton_name, setter_name)): + accessors.setter = setter_name + + var setget_text = "" + if(accessors.setter != null and accessors.getter != null): + setget_text = str("setget ", accessors.setter, ", ", accessors.getter) + else: + # never seen this message show up, but it should show up if we + # get misbehaving singleton. + _lgr.error(str("Could not find setget methods for property: ", + _singleton_name, ".", prop["name"])) + + text += str(prop_text, " ", setget_text, "\n") + + return text + + # ------------------------------------------------------------------------------ # Allows for interacting with a file but only creating a string. This was done # to ease the transition from files being created for doubles to loading @@ -200,8 +322,8 @@ class FileOrString: # ------------------------------------------------------------------------------ # A stroke of genius if I do say so. This allows for doubling a scene without -# having to write any files. By overloading instance we can make whatever -# we want. +# having to write any files. By overloading the "instance" method we can +# make whatever we want. # ------------------------------------------------------------------------------ class PackedSceneDouble: extends PackedScene @@ -240,20 +362,8 @@ var _gut = null var _strategy = null var _base_script_text = _utils.get_file_as_text('res://addons/gut/double_templates/script_template.txt') var _make_files = false - -# These methods all call super implicitly. Stubbing them to call super causes -# super to be called twice. -var _non_super_methods = [ - "_init", - "_ready", - "_notification", - "_enter_world", - "_exit_world", - "_process", - "_physics_process", - "_exit_tree", - "_gui_input ", -] +# used by tests for debugging purposes. +var _print_source = false func _init(strategy=_utils.DOUBLE_STRATEGY.PARTIAL): set_logger(_utils.get_logger()) @@ -270,15 +380,20 @@ func _get_indented_line(indents, text): func _stub_to_call_super(obj_info, method_name): - if(_non_super_methods.has(method_name)): + if(_utils.non_super_methods.has(method_name)): return + var path = obj_info.get_path() - if(obj_info.scene_path != null): + if(obj_info.is_singleton()): + path = obj_info.get_singleton_name() + elif(obj_info.scene_path != null): path = obj_info.scene_path + var params = _utils.StubParams.new(path, method_name, obj_info.get_subpath()) params.to_call_super() _stubber.add_stub(params) + func _get_base_script_text(obj_info, override_path): var path = obj_info.get_path() if(override_path != null): @@ -297,19 +412,34 @@ func _get_base_script_text(obj_info, override_path): gut_id = _gut.get_instance_id() var values = { + # Top sections + "extends":obj_info.get_extends_text(), + "constants":obj_info.get_constants_text(), + "properties":obj_info.get_properties_text(), + + # metadata values "path":path, "subpath":obj_info.get_subpath(), "stubber_id":stubber_id, "spy_id":spy_id, - "extends":obj_info.get_extends_text(), - "gut_id":gut_id + "gut_id":gut_id, + "singleton_name":_utils.nvl(obj_info.get_singleton_name(), ''), + "is_partial":str(obj_info.make_partial_double).to_lower() } return _base_script_text.format(values) + func _write_file(obj_info, dest_path, override_path=null): var base_script = _get_base_script_text(obj_info, override_path) var script_methods = _get_methods(obj_info) + var super_name = "" + var path = "" + + if(obj_info.is_singleton()): + super_name = obj_info.get_singleton_name() + else: + path = obj_info.get_path() var f = FileOrString.new() f._do_file = _make_files @@ -323,17 +453,18 @@ func _write_file(obj_info, dest_path, override_path=null): f.store_string(base_script) for i in range(script_methods.local_methods.size()): - if(obj_info.make_partial_double): - _stub_to_call_super(obj_info, script_methods.local_methods[i].name) - f.store_string(_get_func_text(script_methods.local_methods[i])) + f.store_string(_get_func_text(script_methods.local_methods[i], path, super_name)) for i in range(script_methods.built_ins.size()): _stub_to_call_super(obj_info, script_methods.built_ins[i].name) - f.store_string(_get_func_text(script_methods.built_ins[i])) + f.store_string(_get_func_text(script_methods.built_ins[i], path, super_name)) f.close() + if(_print_source): + print(f.get_contents()) return f + func _double_scene_and_script(scene_info): var to_return = PackedSceneDouble.new() to_return.load_scene(scene_info.get_path()) @@ -353,46 +484,74 @@ func _double_scene_and_script(scene_info): return to_return + func _get_methods(object_info): var obj = object_info.instantiate() # any method in the script or super script var script_methods = ScriptMethods.new() var methods = obj.get_method_list() - if(!(obj is Reference)): + + if(!object_info.is_singleton() and !(obj is Reference)): obj.free() # first pass is for local methods only for i in range(methods.size()): + if(object_info.is_singleton()): + #print(methods[i].name, " :: ", methods[i].flags, " :: ", methods[i].id) + #print(" ", methods[i]) + + # It appears that the ID for methods upstream from a singleton are + # below 200. Initially it was thought that singleton specific methods + # were above 1000. This was true for Input but not for OS. I've + # changed the condition to be > 200 instead of > 1000. It will take + # some investigation to figure out if this is right, but it works + # for now. Someone either find an issue and open a bug, or this will + # just exist like this. Sorry future me (or someone else). + if(methods[i].id > 200 and methods[i].flags in [1, 9]): + script_methods.add_local_method(methods[i]) + # 65 is a magic number for methods in script, though documentation # says 64. This picks up local overloads of base class methods too. - if(methods[i].flags == 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): + # See MethodFlags in @GlobalScope + elif(methods[i].flags == 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): script_methods.add_local_method(methods[i]) - if(object_info.get_method_strategy() == _utils.DOUBLE_STRATEGY.FULL): # second pass is for anything not local - for i in range(methods.size()): + for j in range(methods.size()): # 65 is a magic number for methods in script, though documentation # says 64. This picks up local overloads of base class methods too. - if(methods[i].flags != 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): - script_methods.add_built_in_method(methods[i]) + if(methods[j].flags != 65 and !_ignored_methods.has(object_info.get_path(), methods[j]['name'])): + script_methods.add_built_in_method(methods[j]) return script_methods + func _get_inst_id_ref_str(inst): var ref_str = 'null' if(inst): ref_str = str('instance_from_id(', inst.get_instance_id(),')') return ref_str -func _get_func_text(method_hash): - return _method_maker.get_function_text(method_hash) + "\n" + +func _get_func_text(method_hash, path, super=""): + var override_count = null; + if(_stubber != null): + override_count = _stubber.get_parameter_count(path, method_hash.name) + + var text = _method_maker.get_function_text(method_hash, path, override_count, super) + "\n" + + return text # returns the path to write the double file to func _get_temp_path(object_info): var file_name = null var extension = null - if(object_info.is_native()): + + if(object_info.is_singleton()): + file_name = str(object_info.get_singleton_instance()) + extension = "gd" + elif(object_info.is_native()): file_name = object_info.get_native_class_name() extension = 'gd' else: @@ -407,8 +566,6 @@ func _get_temp_path(object_info): var to_return = _output_dir.plus_file(file_name) return to_return -func _load_double(fileOrString): - return fileOrString.load_it() func _double(obj_info, override_path=null): var temp_path = _get_temp_path(obj_info) @@ -416,24 +573,28 @@ func _double(obj_info, override_path=null): _double_count += 1 return result + func _double_script(path, make_partial, strategy): var oi = ObjectInfo.new(path) oi.make_partial_double = make_partial oi.set_method_strategy(strategy) return _double(oi).load_it() + func _double_inner(path, subpath, make_partial, strategy): var oi = ObjectInfo.new(path, subpath) oi.set_method_strategy(strategy) oi.make_partial_double = make_partial return _double(oi).load_it() + func _double_scene(path, make_partial, strategy): var oi = ObjectInfo.new(path) oi.set_method_strategy(strategy) oi.make_partial_double = make_partial return _double_scene_and_script(oi) + func _double_gdnative(native_class, make_partial, strategy): var oi = ObjectInfo.new(null) oi.set_native_class(native_class) @@ -441,12 +602,21 @@ func _double_gdnative(native_class, make_partial, strategy): oi.make_partial_double = make_partial return _double(oi).load_it() + +func _double_singleton(singleton_name, make_partial, strategy): + var oi = ObjectInfo.new(null) + oi.set_singleton_name(singleton_name) + oi.set_method_strategy(_utils.DOUBLE_STRATEGY.PARTIAL) + oi.make_partial_double = make_partial + return _double(oi).load_it() + # ############### # Public # ############### func get_output_dir(): return _output_dir + func set_output_dir(output_dir): if(output_dir != null): _output_dir = output_dir @@ -454,68 +624,95 @@ func set_output_dir(output_dir): var d = Directory.new() d.make_dir_recursive(output_dir) + func get_spy(): return _spy + func set_spy(spy): _spy = spy + func get_stubber(): return _stubber + func set_stubber(stubber): _stubber = stubber + func get_logger(): return _lgr + func set_logger(logger): _lgr = logger _method_maker.set_logger(logger) + func get_strategy(): return _strategy + func set_strategy(strategy): _strategy = strategy + func get_gut(): return _gut + func set_gut(gut): _gut = gut + func partial_double_scene(path, strategy=_strategy): return _double_scene(path, true, strategy) + # double a scene func double_scene(path, strategy=_strategy): return _double_scene(path, false, strategy) + # double a script/object func double(path, strategy=_strategy): return _double_script(path, false, strategy) + func partial_double(path, strategy=_strategy): return _double_script(path, true, strategy) + func partial_double_inner(path, subpath, strategy=_strategy): return _double_inner(path, subpath, true, strategy) + # double an inner class in a script func double_inner(path, subpath, strategy=_strategy): return _double_inner(path, subpath, false, strategy) + # must always use FULL strategy since this is a native class and you won't get # any methods if you don't use FULL func double_gdnative(native_class): return _double_gdnative(native_class, false, _utils.DOUBLE_STRATEGY.FULL) + # must always use FULL strategy since this is a native class and you won't get # any methods if you don't use FULL func partial_double_gdnative(native_class): return _double_gdnative(native_class, true, _utils.DOUBLE_STRATEGY.FULL) + +func double_singleton(name): + return _double_singleton(name, false, _utils.DOUBLE_STRATEGY.PARTIAL) + + +func partial_double_singleton(name): + return _double_singleton(name, true, _utils.DOUBLE_STRATEGY.PARTIAL) + + func clear_output_directory(): if(!_make_files): return false @@ -542,15 +739,22 @@ func delete_output_directory(): var d = Directory.new() d.remove(_output_dir) + func add_ignored_method(path, method_name): _ignored_methods.add(path, method_name) + func get_ignored_methods(): return _ignored_methods + func get_make_files(): return _make_files + func set_make_files(make_files): _make_files = make_files set_output_dir(_output_dir) + +func get_method_maker(): + return _method_maker \ No newline at end of file diff --git a/addons/gut/get_native_script.gd b/addons/gut/get_native_script.gd new file mode 100644 index 00000000..7055fc39 --- /dev/null +++ b/addons/gut/get_native_script.gd @@ -0,0 +1,6 @@ +# Since NativeScript does not exist if GDNative is not included in the build +# of Godot this script is conditionally loaded only when NativeScript exists. +# You can then get a reference to NativeScript for use in `is` checks by calling +# get_it. +static func get_it(): + return NativeScript diff --git a/addons/gut/gui/BottomPanelShortcuts.gd b/addons/gut/gui/BottomPanelShortcuts.gd new file mode 100644 index 00000000..86fbf8dc --- /dev/null +++ b/addons/gut/gui/BottomPanelShortcuts.gd @@ -0,0 +1,82 @@ +tool +extends WindowDialog + +onready var _ctrls = { + run_all = $Layout/CRunAll/ShortcutButton, + run_current_script = $Layout/CRunCurrentScript/ShortcutButton, + run_current_inner = $Layout/CRunCurrentInner/ShortcutButton, + run_current_test = $Layout/CRunCurrentTest/ShortcutButton, + panel_button = $Layout/CPanelButton/ShortcutButton, +} + +func _ready(): + for key in _ctrls: + var sc_button = _ctrls[key] + sc_button.connect('start_edit', self, '_on_edit_start', [sc_button]) + sc_button.connect('end_edit', self, '_on_edit_end') + + + # show dialog when running scene from editor. + if(get_parent() == get_tree().root): + popup_centered() + +# ------------ +# Events +# ------------ +func _on_Hide_pressed(): + hide() + +func _on_edit_start(which): + for key in _ctrls: + var sc_button = _ctrls[key] + if(sc_button != which): + sc_button.disable_set(true) + sc_button.disable_clear(true) + +func _on_edit_end(): + for key in _ctrls: + var sc_button = _ctrls[key] + sc_button.disable_set(false) + sc_button.disable_clear(false) + +# ------------ +# Public +# ------------ +func get_run_all(): + return _ctrls.run_all.get_shortcut() + +func get_run_current_script(): + return _ctrls.run_current_script.get_shortcut() + +func get_run_current_inner(): + return _ctrls.run_current_inner.get_shortcut() + +func get_run_current_test(): + return _ctrls.run_current_test.get_shortcut() + +func get_panel_button(): + return _ctrls.panel_button.get_shortcut() + + +func save_shortcuts(path): + var f = ConfigFile.new() + + f.set_value('main', 'run_all', _ctrls.run_all.get_shortcut()) + f.set_value('main', 'run_current_script', _ctrls.run_current_script.get_shortcut()) + f.set_value('main', 'run_current_inner', _ctrls.run_current_inner.get_shortcut()) + f.set_value('main', 'run_current_test', _ctrls.run_current_test.get_shortcut()) + f.set_value('main', 'panel_button', _ctrls.panel_button.get_shortcut()) + + f.save(path) + + +func load_shortcuts(path): + var emptyShortcut = ShortCut.new() + var f = ConfigFile.new() + f.load(path) + + _ctrls.run_all.set_shortcut(f.get_value('main', 'run_all', emptyShortcut)) + _ctrls.run_current_script.set_shortcut(f.get_value('main', 'run_current_script', emptyShortcut)) + _ctrls.run_current_inner.set_shortcut(f.get_value('main', 'run_current_inner', emptyShortcut)) + _ctrls.run_current_test.set_shortcut(f.get_value('main', 'run_current_test', emptyShortcut)) + _ctrls.panel_button.set_shortcut(f.get_value('main', 'panel_button', emptyShortcut)) diff --git a/addons/gut/gui/BottomPanelShortcuts.tscn b/addons/gut/gui/BottomPanelShortcuts.tscn new file mode 100644 index 00000000..5e2e5d97 --- /dev/null +++ b/addons/gut/gui/BottomPanelShortcuts.tscn @@ -0,0 +1,232 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://addons/gut/gui/ShortcutButton.tscn" type="PackedScene" id=1] +[ext_resource path="res://addons/gut/gui/BottomPanelShortcuts.gd" type="Script" id=2] + +[node name="BottomPanelShortcuts" type="WindowDialog"] +visible = true +anchor_right = 0.234 +anchor_bottom = 0.328 +margin_right = 195.384 +margin_bottom = 62.2 +rect_min_size = Vector2( 435, 305 ) +popup_exclusive = true +window_title = "GUT Shortcuts" +resizable = true +script = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Layout" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 5.0 +margin_right = -5.0 +margin_bottom = 2.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TopPad" type="CenterContainer" parent="Layout"] +margin_right = 425.0 +margin_bottom = 5.0 +rect_min_size = Vector2( 0, 5 ) + +[node name="Label2" type="Label" parent="Layout"] +margin_top = 9.0 +margin_right = 425.0 +margin_bottom = 29.0 +rect_min_size = Vector2( 0, 20 ) +text = "Always Active" +align = 1 +valign = 1 +autowrap = true + +[node name="ColorRect" type="ColorRect" parent="Layout/Label2"] +show_behind_parent = true +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color( 0, 0, 0, 0.196078 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CPanelButton" type="HBoxContainer" parent="Layout"] +margin_top = 33.0 +margin_right = 425.0 +margin_bottom = 58.0 + +[node name="Label" type="Label" parent="Layout/CPanelButton"] +margin_right = 138.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Show/Hide GUT Panel" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CPanelButton" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 142.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="GutPanelPad" type="CenterContainer" parent="Layout"] +margin_top = 62.0 +margin_right = 425.0 +margin_bottom = 67.0 +rect_min_size = Vector2( 0, 5 ) + +[node name="Label" type="Label" parent="Layout"] +margin_top = 71.0 +margin_right = 425.0 +margin_bottom = 91.0 +rect_min_size = Vector2( 0, 20 ) +text = "Only Active When GUT Panel Shown" +align = 1 +valign = 1 +autowrap = true + +[node name="ColorRect2" type="ColorRect" parent="Layout/Label"] +show_behind_parent = true +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color( 0, 0, 0, 0.196078 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TopPad2" type="CenterContainer" parent="Layout"] +margin_top = 95.0 +margin_right = 425.0 +margin_bottom = 100.0 +rect_min_size = Vector2( 0, 5 ) + +[node name="CRunAll" type="HBoxContainer" parent="Layout"] +margin_top = 104.0 +margin_right = 425.0 +margin_bottom = 129.0 + +[node name="Label" type="Label" parent="Layout/CRunAll"] +margin_right = 50.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run All" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunAll" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 54.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CRunCurrentScript" type="HBoxContainer" parent="Layout"] +margin_top = 133.0 +margin_right = 425.0 +margin_bottom = 158.0 + +[node name="Label" type="Label" parent="Layout/CRunCurrentScript"] +margin_right = 115.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run Current Script" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunCurrentScript" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 119.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CRunCurrentInner" type="HBoxContainer" parent="Layout"] +margin_top = 162.0 +margin_right = 425.0 +margin_bottom = 187.0 + +[node name="Label" type="Label" parent="Layout/CRunCurrentInner"] +margin_right = 150.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run Current Inner Class" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunCurrentInner" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 154.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CRunCurrentTest" type="HBoxContainer" parent="Layout"] +margin_top = 191.0 +margin_right = 425.0 +margin_bottom = 216.0 + +[node name="Label" type="Label" parent="Layout/CRunCurrentTest"] +margin_right = 106.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run Current Test" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunCurrentTest" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 110.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CenterContainer2" type="CenterContainer" parent="Layout"] +margin_top = 220.0 +margin_right = 425.0 +margin_bottom = 241.0 +rect_min_size = Vector2( 0, 5 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ShiftDisclaimer" type="Label" parent="Layout"] +margin_top = 245.0 +margin_right = 425.0 +margin_bottom = 259.0 +text = "\"Shift\" cannot be the only modifier for a shortcut." +align = 2 +autowrap = true + +[node name="HBoxContainer" type="HBoxContainer" parent="Layout"] +margin_top = 263.0 +margin_right = 425.0 +margin_bottom = 293.0 + +[node name="CenterContainer" type="CenterContainer" parent="Layout/HBoxContainer"] +margin_right = 361.0 +margin_bottom = 30.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Hide" type="Button" parent="Layout/HBoxContainer"] +margin_left = 365.0 +margin_right = 425.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 60, 30 ) +text = "Close" + +[node name="BottomPad" type="CenterContainer" parent="Layout"] +margin_top = 297.0 +margin_right = 425.0 +margin_bottom = 307.0 +rect_min_size = Vector2( 0, 10 ) +size_flags_horizontal = 3 + +[connection signal="pressed" from="Layout/HBoxContainer/Hide" to="." method="_on_Hide_pressed"] diff --git a/addons/gut/gui/GutBottomPanel.gd b/addons/gut/gui/GutBottomPanel.gd new file mode 100644 index 00000000..50a32d87 --- /dev/null +++ b/addons/gut/gui/GutBottomPanel.gd @@ -0,0 +1,326 @@ +tool +extends Control + +const RUNNER_JSON_PATH = 'res://.gut_editor_config.json' +const RESULT_FILE = 'user://.gut_editor.bbcode' +const RESULT_JSON = 'user://.gut_editor.json' +const SHORTCUTS_PATH = 'res://.gut_editor_shortcuts.cfg' + +var TestScript = load('res://addons/gut/test.gd') +var GutConfigGui = load('res://addons/gut/gui/gut_config_gui.gd') + +var _interface = null; +var _is_running = false; +var _gut_config = load('res://addons/gut/gut_config.gd').new() +var _gut_config_gui = null +var _gut_plugin = null +var _light_color = Color(0, 0, 0, .5) +var _panel_button = null +var _last_selected_path = null + + +onready var _ctrls = { + output = $layout/RSplit/CResults/Output, + run_button = $layout/ControlBar/RunAll, + settings = $layout/RSplit/sc/Settings, + shortcut_dialog = $BottomPanelShortcuts, + light = $layout/RSplit/CResults/ControlBar/Light, + results = { + passing = $layout/RSplit/CResults/ControlBar/Passing/value, + failing = $layout/RSplit/CResults/ControlBar/Failing/value, + pending = $layout/RSplit/CResults/ControlBar/Pending/value, + errors = $layout/RSplit/CResults/ControlBar/Errors/value, + warnings = $layout/RSplit/CResults/ControlBar/Warnings/value, + orphans = $layout/RSplit/CResults/ControlBar/Orphans/value + }, + run_at_cursor = $layout/ControlBar/RunAtCursor +} + + +func _init(): + _gut_config.load_panel_options(RUNNER_JSON_PATH) + + +func _ready(): + _gut_config_gui = GutConfigGui.new(_ctrls.settings) + _gut_config_gui.set_options(_gut_config.options) + _set_all_fonts_in_ftl(_ctrls.output, _gut_config.options.panel_options.font_name) + _set_font_size_for_rtl(_ctrls.output, _gut_config.options.panel_options.font_size) + + +func _process(delta): + if(_is_running): + if(!_interface.is_playing_scene()): + _is_running = false + _ctrls.output.add_text("\ndone") + load_result_output() + _gut_plugin.make_bottom_panel_item_visible(self) + +# --------------- +# Private +# --------------- + +func load_shortcuts(): + _ctrls.shortcut_dialog.load_shortcuts(SHORTCUTS_PATH) + _apply_shortcuts() + + +# ----------------------------------- +func _set_font(rtl, font_name, custom_name): + if(font_name == null): + rtl.set('custom_fonts/' + custom_name, null) + else: + var dyn_font = DynamicFont.new() + var font_data = DynamicFontData.new() + font_data.font_path = 'res://addons/gut/fonts/' + font_name + '.ttf' + font_data.antialiased = true + dyn_font.font_data = font_data + rtl.set('custom_fonts/' + custom_name, dyn_font) + + +func _set_all_fonts_in_ftl(ftl, base_name): + if(base_name == 'Default'): + _set_font(ftl, null, 'normal_font') + _set_font(ftl, null, 'bold_font') + _set_font(ftl, null, 'italics_font') + _set_font(ftl, null, 'bold_italics_font') + else: + _set_font(ftl, base_name + '-Regular', 'normal_font') + _set_font(ftl, base_name + '-Bold', 'bold_font') + _set_font(ftl, base_name + '-Italic', 'italics_font') + _set_font(ftl, base_name + '-BoldItalic', 'bold_italics_font') + + +func _set_font_size_for_rtl(rtl, new_size): + if(rtl.get('custom_fonts/normal_font') != null): + rtl.get('custom_fonts/bold_italics_font').size = new_size + rtl.get('custom_fonts/bold_font').size = new_size + rtl.get('custom_fonts/italics_font').size = new_size + rtl.get('custom_fonts/normal_font').size = new_size +# ----------------------------------- + + +func _is_test_script(script): + var from = script.get_base_script() + while(from and from.resource_path != 'res://addons/gut/test.gd'): + from = from.get_base_script() + + return from != null + + +func _update_last_run_label(): + var text = '' + + if( _gut_config.options.selected == null and + _gut_config.options.inner_class == null and + _gut_config.options.unit_test_name == null): + text = 'All' + else: + text = nvl(_gut_config.options.selected, '') + ' ' + text += nvl(_gut_config.options.inner_class, '') + ' ' + text += nvl(_gut_config.options.unit_test_name, '') + + + +func _show_errors(errs): + _ctrls.output.clear() + var text = "Cannot run tests, you have a conrfiguration error:\n" + for e in errs: + text += str('* ', e, "\n") + text += "[right]Check your settings here ----->[/right]" + _ctrls.output.bbcode_text = text + + +func _run_tests(): + var issues = _gut_config_gui.get_config_issues() + if(issues.size() > 0): + _show_errors(issues) + return + + write_file(RESULT_FILE, 'Run in progress') + _gut_config.options = _gut_config_gui.get_options(_gut_config.options) + _set_all_fonts_in_ftl(_ctrls.output, _gut_config.options.panel_options.font_name) + _set_font_size_for_rtl(_ctrls.output, _gut_config.options.panel_options.font_size) + + var w_result = _gut_config.write_options(RUNNER_JSON_PATH) + if(w_result != OK): + push_error(str('Could not write options to ', RUNNER_JSON_PATH, ': ', w_result)) + return; + + _ctrls.output.clear() + + _update_last_run_label() + _interface.play_custom_scene('res://addons/gut/gui/GutRunner.tscn') + + _is_running = true + _ctrls.output.add_text('running...') + + +func _apply_shortcuts(): + _ctrls.run_button.shortcut = _ctrls.shortcut_dialog.get_run_all() + + _ctrls.run_at_cursor.get_script_button().shortcut = \ + _ctrls.shortcut_dialog.get_run_current_script() + _ctrls.run_at_cursor.get_inner_button().shortcut = \ + _ctrls.shortcut_dialog.get_run_current_inner() + _ctrls.run_at_cursor.get_test_button().shortcut = \ + _ctrls.shortcut_dialog.get_run_current_test() + + _panel_button.shortcut = _ctrls.shortcut_dialog.get_panel_button() + + +func _run_all(): + _gut_config.options.selected = null + _gut_config.options.inner_class = null + _gut_config.options.unit_test_name = null + + _run_tests() + + +# --------------- +# Events +# --------------- +func _on_editor_script_changed(script): + if(script): + set_current_script(script) + + +func _on_RunAll_pressed(): + _on_RunTests_pressed() + + +func _on_RunTests_pressed(): + _run_all() + + +func _on_CopyButton_pressed(): + OS.clipboard = _ctrls.output.text + + +func _on_ClearButton_pressed(): + _ctrls.output.clear() + + +func _on_Shortcuts_pressed(): + _ctrls.shortcut_dialog.popup_centered() + + +func _on_BottomPanelShortcuts_popup_hide(): + _apply_shortcuts() + _ctrls.shortcut_dialog.save_shortcuts(SHORTCUTS_PATH) + + +func _on_Light_draw(): + var l = _ctrls.light + l.draw_circle(Vector2(l.rect_size.x / 2, l.rect_size.y / 2), l.rect_size.x / 2, _light_color) + + +func _on_RunAtCursor_run_tests(what): + _gut_config.options.selected = what.script + _gut_config.options.inner_class = what.inner_class + _gut_config.options.unit_test_name = what.test_method + + _run_tests() + + +# --------------- +# Public +# --------------- + +func load_result_output(): + _ctrls.output.bbcode_text = get_file_as_text(RESULT_FILE) + _ctrls.output.grab_focus() + _ctrls.output.scroll_to_line(_ctrls.output.get_line_count() -1) + + var summary = get_file_as_text(RESULT_JSON) + var results = JSON.parse(summary) + if(results.error != OK): + return + var summary_json = results.result['test_scripts']['props'] + _ctrls.results.passing.text = str(summary_json.passing) + _ctrls.results.passing.get_parent().visible = true + + _ctrls.results.failing.text = str(summary_json.failures) + _ctrls.results.failing.get_parent().visible = true + + _ctrls.results.pending.text = str(summary_json.pending) + _ctrls.results.pending.get_parent().visible = _ctrls.results.pending.text != '0' + + _ctrls.results.errors.text = str(summary_json.errors) + _ctrls.results.errors.get_parent().visible = _ctrls.results.errors.text != '0' + + _ctrls.results.warnings.text = str(summary_json.warnings) + _ctrls.results.warnings.get_parent().visible = _ctrls.results.warnings.text != '0' + + _ctrls.results.orphans.text = str(summary_json.orphans) + _ctrls.results.orphans.get_parent().visible = _ctrls.results.orphans.text != '0' and !_gut_config.options.hide_orphans + + if(summary_json.tests == 0): + _light_color = Color(1, 0, 0, .75) + elif(summary_json.failures != 0): + _light_color = Color(1, 0, 0, .75) + elif(summary_json.pending != 0): + _light_color = Color(1, 1, 0, .75) + else: + _light_color = Color(0, 1, 0, .75) + _ctrls.light.update() + + + +func set_current_script(script): + if(script): + if(_is_test_script(script)): + var file = script.resource_path.get_file() + _last_selected_path = script.resource_path.get_file() + _ctrls.run_at_cursor.activate_for_script(script.resource_path) + + +func set_interface(value): + _interface = value + _interface.get_script_editor().connect("editor_script_changed", self, '_on_editor_script_changed') + _ctrls.run_at_cursor.set_script_editor(_interface.get_script_editor()) + set_current_script(_interface.get_script_editor().get_current_script()) + + +func set_plugin(value): + _gut_plugin = value + + +func set_panel_button(value): + _panel_button = value + +# ------------------------------------------------------------------------------ +# Write a file. +# ------------------------------------------------------------------------------ +func write_file(path, content): + var f = File.new() + var result = f.open(path, f.WRITE) + if(result == OK): + f.store_string(content) + f.close() + return result + + +# ------------------------------------------------------------------------------ +# Returns the text of a file or an empty string if the file could not be opened. +# ------------------------------------------------------------------------------ +func get_file_as_text(path): + var to_return = '' + var f = File.new() + var result = f.open(path, f.READ) + if(result == OK): + to_return = f.get_as_text() + f.close() + return to_return + + +# ------------------------------------------------------------------------------ +# return if_null if value is null otherwise return value +# ------------------------------------------------------------------------------ +func nvl(value, if_null): + if(value == null): + return if_null + else: + return value + + diff --git a/addons/gut/gui/GutBottomPanel.tscn b/addons/gut/gui/GutBottomPanel.tscn new file mode 100644 index 00000000..0bb7fc37 --- /dev/null +++ b/addons/gut/gui/GutBottomPanel.tscn @@ -0,0 +1,386 @@ +[gd_scene load_steps=16 format=2] + +[ext_resource path="res://addons/gut/gui/GutBottomPanel.gd" type="Script" id=1] +[ext_resource path="res://addons/gut/gui/BottomPanelShortcuts.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/gut/gui/RunAtCursor.tscn" type="PackedScene" id=3] +[ext_resource path="res://addons/gut/gui/play.png" type="Texture" id=4] + +[sub_resource type="InputEventKey" id=8] +control = true +scancode = 49 + +[sub_resource type="ShortCut" id=9] +shortcut = SubResource( 8 ) + +[sub_resource type="StyleBoxEmpty" id=5] + +[sub_resource type="DynamicFontData" id=10] +font_path = "res://addons/gut/fonts/CourierPrime-BoldItalic.ttf" + +[sub_resource type="DynamicFont" id=11] +size = 30 +font_data = SubResource( 10 ) + +[sub_resource type="DynamicFontData" id=12] +font_path = "res://addons/gut/fonts/CourierPrime-Italic.ttf" + +[sub_resource type="DynamicFont" id=13] +size = 30 +font_data = SubResource( 12 ) + +[sub_resource type="DynamicFontData" id=14] +font_path = "res://addons/gut/fonts/CourierPrime-Bold.ttf" + +[sub_resource type="DynamicFont" id=15] +size = 30 +font_data = SubResource( 14 ) + +[sub_resource type="DynamicFontData" id=16] +font_path = "res://addons/gut/fonts/CourierPrime-Regular.ttf" + +[sub_resource type="DynamicFont" id=17] +size = 30 +font_data = SubResource( 16 ) + +[node name="GutBottomPanel" type="Control"] +anchor_left = -0.0025866 +anchor_top = -0.00176575 +anchor_right = 0.997413 +anchor_bottom = 0.998234 +margin_left = 2.64868 +margin_top = 1.05945 +margin_right = 2.64862 +margin_bottom = 1.05945 +rect_min_size = Vector2( 0, 300 ) +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="layout" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ControlBar" type="HBoxContainer" parent="layout"] +margin_right = 1023.0 +margin_bottom = 40.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="RunAll" type="Button" parent="layout/ControlBar"] +margin_right = 150.0 +margin_bottom = 40.0 +rect_min_size = Vector2( 150, 0 ) +hint_tooltip = "Run all test scripts in the suite." +size_flags_vertical = 11 +shortcut = SubResource( 9 ) +text = "Run All" +icon = ExtResource( 4 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="layout/ControlBar"] +margin_left = 154.0 +margin_top = 13.0 +margin_right = 213.0 +margin_bottom = 27.0 +hint_tooltip = "When a test script is edited, buttons are displayed to +run the opened script or an Inner-Test-Class or a +single test. The buttons change based on the location +of the cursor in the file. + +These buttons will remain active when editing other +items so that you can run tests without having to switch +back to the test script. + +You can assign keyboard shortcuts for these buttons +using the \"shortcuts\" button in the GUT panel." +mouse_filter = 1 +text = "Current: " + +[node name="RunAtCursor" parent="layout/ControlBar" instance=ExtResource( 3 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 217.0 +margin_right = 456.0 +margin_bottom = 40.0 +rect_min_size = Vector2( 0, 40 ) + +[node name="CenterContainer2" type="CenterContainer" parent="layout/ControlBar"] +margin_left = 460.0 +margin_right = 699.0 +margin_bottom = 40.0 +size_flags_horizontal = 3 + +[node name="FocusButton" type="Button" parent="layout/ControlBar"] +show_behind_parent = true +margin_left = 703.0 +margin_right = 703.0 +margin_bottom = 40.0 +custom_styles/normal = SubResource( 5 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CenterContainer" type="CenterContainer" parent="layout/ControlBar"] +margin_left = 707.0 +margin_right = 946.0 +margin_bottom = 40.0 +size_flags_horizontal = 3 + +[node name="Shortcuts" type="Button" parent="layout/ControlBar"] +margin_left = 950.0 +margin_right = 1022.0 +margin_bottom = 40.0 +hint_tooltip = "Set shortcuts for GUT buttons. Shortcuts do not work when the GUT panel is not visible." +size_flags_vertical = 11 +text = "Shortcuts" + +[node name="RSplit" type="HSplitContainer" parent="layout"] +margin_top = 44.0 +margin_right = 1023.0 +margin_bottom = 599.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="CResults" type="VBoxContainer" parent="layout/RSplit"] +margin_right = 611.0 +margin_bottom = 555.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ControlBar" type="HBoxContainer" parent="layout/RSplit/CResults"] +margin_right = 611.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 0, 35 ) + +[node name="Light" type="Control" parent="layout/RSplit/CResults/ControlBar"] +margin_right = 30.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 30, 30 ) + +[node name="Passing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 107.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Passing"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 54.0 +margin_bottom = 24.0 +text = "Passing" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"] +margin_left = 58.0 +margin_top = 10.0 +margin_right = 73.0 +margin_bottom = 24.0 +text = "---" + +[node name="Failing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 100.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Failing"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 47.0 +margin_bottom = 24.0 +text = "Failing" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"] +margin_left = 51.0 +margin_top = 10.0 +margin_right = 66.0 +margin_bottom = 24.0 +text = "---" + +[node name="Pending" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 110.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Pending"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 57.0 +margin_bottom = 24.0 +text = "Pending" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"] +margin_left = 61.0 +margin_top = 10.0 +margin_right = 76.0 +margin_bottom = 24.0 +text = "---" + +[node name="Orphans" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 110.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Orphans"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 57.0 +margin_bottom = 24.0 +text = "Orphans" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"] +margin_left = 61.0 +margin_top = 10.0 +margin_right = 76.0 +margin_bottom = 24.0 +text = "---" + +[node name="Errors" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 96.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Errors"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 43.0 +margin_bottom = 24.0 +hint_tooltip = "The number of GUT errors generated. This does not include engine errors." +text = "Errors" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"] +margin_left = 47.0 +margin_top = 10.0 +margin_right = 62.0 +margin_bottom = 24.0 +text = "---" + +[node name="Warnings" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 118.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Warnings"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 65.0 +margin_bottom = 24.0 +text = "Warnings" +__meta__ = { +"_editor_description_": "The number of GUT Warnings generated. This does not include engine warnings." +} + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"] +margin_left = 69.0 +margin_top = 10.0 +margin_right = 84.0 +margin_bottom = 24.0 +text = "---" + +[node name="CenterContainer" type="CenterContainer" parent="layout/RSplit/CResults/ControlBar"] +margin_left = 34.0 +margin_right = 488.0 +margin_bottom = 35.0 +size_flags_horizontal = 3 + +[node name="CopyButton" type="Button" parent="layout/RSplit/CResults/ControlBar"] +margin_left = 492.0 +margin_right = 547.0 +margin_bottom = 35.0 +text = " Copy " + +[node name="ClearButton" type="Button" parent="layout/RSplit/CResults/ControlBar"] +margin_left = 551.0 +margin_right = 611.0 +margin_bottom = 35.0 +text = " Clear " + +[node name="Output" type="RichTextLabel" parent="layout/RSplit/CResults"] +margin_top = 39.0 +margin_right = 611.0 +margin_bottom = 555.0 +focus_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +custom_fonts/bold_italics_font = SubResource( 11 ) +custom_fonts/italics_font = SubResource( 13 ) +custom_fonts/bold_font = SubResource( 15 ) +custom_fonts/normal_font = SubResource( 17 ) +bbcode_enabled = true +scroll_following = true +selection_enabled = true + +[node name="sc" type="ScrollContainer" parent="layout/RSplit"] +margin_left = 623.0 +margin_right = 1023.0 +margin_bottom = 555.0 +rect_min_size = Vector2( 400, 0 ) +mouse_filter = 1 +size_flags_vertical = 3 + +[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"] +margin_right = 388.0 +margin_bottom = 862.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="BottomPanelShortcuts" parent="." instance=ExtResource( 2 )] +visible = false +anchor_left = -0.000517324 +anchor_top = 0.000882874 +anchor_right = 0.233483 +anchor_bottom = 0.328883 +margin_left = 10.0649 +margin_top = -173.752 +margin_right = 31.6969 +margin_bottom = -125.552 + +[connection signal="pressed" from="layout/ControlBar/RunAll" to="." method="_on_RunAll_pressed"] +[connection signal="run_tests" from="layout/ControlBar/RunAtCursor" to="." method="_on_RunAtCursor_run_tests"] +[connection signal="pressed" from="layout/ControlBar/FocusButton" to="." method="_on_FocusButton_pressed"] +[connection signal="pressed" from="layout/ControlBar/Shortcuts" to="." method="_on_Shortcuts_pressed"] +[connection signal="draw" from="layout/RSplit/CResults/ControlBar/Light" to="." method="_on_Light_draw"] +[connection signal="pressed" from="layout/RSplit/CResults/ControlBar/CopyButton" to="." method="_on_CopyButton_pressed"] +[connection signal="pressed" from="layout/RSplit/CResults/ControlBar/ClearButton" to="." method="_on_ClearButton_pressed"] +[connection signal="popup_hide" from="BottomPanelShortcuts" to="." method="_on_BottomPanelShortcuts_popup_hide"] diff --git a/addons/gut/gui/GutRunner.gd b/addons/gut/gui/GutRunner.gd new file mode 100644 index 00000000..b2b16f27 --- /dev/null +++ b/addons/gut/gui/GutRunner.gd @@ -0,0 +1,98 @@ +extends Node2D + +var Gut = load('res://addons/gut/gut.gd') +var ResultExporter = load('res://addons/gut/result_exporter.gd') +var GutConfig = load('res://addons/gut/gut_config.gd') + +const RUNNER_JSON_PATH = 'res://.gut_editor_config.json' +const RESULT_FILE = 'user://.gut_editor.bbcode' +const RESULT_JSON = 'user://.gut_editor.json' + +var _gut_config = null +var _gut = null; +var _wrote_results = false +# Flag for when this is being used at the command line. Otherwise it is +# assumed this is being used by the panel and being launched with +# play_custom_scene +var _cmdln_mode = false + +onready var _gut_layer = $GutLayer + + +func _ready(): + if(_gut_config == null): + _gut_config = GutConfig.new() + _gut_config.load_options(RUNNER_JSON_PATH) + + + # The command line will call run_tests on its own. When used from the panel + # we have to kick off the tests ourselves b/c there's no way I know of to + # interact with the scene that was run via play_custom_scene. + if(!_cmdln_mode): + call_deferred('run_tests') + + +func run_tests(): + if(_gut == null): + _gut = Gut.new() + + _gut.set_add_children_to(self) + if(_gut_config.options.gut_on_top): + _gut_layer.add_child(_gut) + else: + add_child(_gut) + + if(!_cmdln_mode): + _gut.connect('tests_finished', self, '_on_tests_finished', + [_gut_config.options.should_exit, _gut_config.options.should_exit_on_success]) + + _gut_config.config_gut(_gut) + if(_gut_config.options.gut_on_top): + _gut.get_gui().goto_bottom_right_corner() + + var run_rest_of_scripts = _gut_config.options.unit_test_name == '' + _gut.test_scripts(run_rest_of_scripts) + + +func _write_results(): + # bbcode_text appears to be empty. I'm not 100% sure why. Until that is + # figured out we have to just get the text which stinks. + var content = _gut.get_gui().get_text_box().text + + var f = File.new() + var result = f.open(RESULT_FILE, f.WRITE) + if(result == OK): + f.store_string(content) + f.close() + else: + print('ERROR Could not save bbcode, result = ', result) + + var exporter = ResultExporter.new() + var f_result = exporter.write_summary_file(_gut, RESULT_JSON) + _wrote_results = true + + +func _exit_tree(): + if(!_wrote_results and !_cmdln_mode): + _write_results() + + +func _on_tests_finished(should_exit, should_exit_on_success): + _write_results() + + if(should_exit): + get_tree().quit() + elif(should_exit_on_success and _gut.get_fail_count() == 0): + get_tree().quit() + + +func get_gut(): + if(_gut == null): + _gut = Gut.new() + return _gut + +func set_gut_config(which): + _gut_config = which + +func set_cmdln_mode(is_it): + _cmdln_mode = is_it diff --git a/addons/gut/gui/GutRunner.tscn b/addons/gut/gui/GutRunner.tscn new file mode 100644 index 00000000..077e4113 --- /dev/null +++ b/addons/gut/gui/GutRunner.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/gut/gui/GutRunner.gd" type="Script" id=1] + +[node name="GutRunner" type="Node2D"] +script = ExtResource( 1 ) + +[node name="GutLayer" type="CanvasLayer" parent="."] +layer = 128 diff --git a/addons/gut/gui/GutSceneTheme.tres b/addons/gut/gui/GutSceneTheme.tres new file mode 100644 index 00000000..565a6af9 --- /dev/null +++ b/addons/gut/gui/GutSceneTheme.tres @@ -0,0 +1,11 @@ +[gd_resource type="Theme" load_steps=3 format=2] + +[sub_resource type="DynamicFontData" id=9] +font_path = "res://addons/gut/fonts/AnonymousPro-Regular.ttf" + +[sub_resource type="DynamicFont" id=10] +size = 14 +font_data = SubResource( 9 ) + +[resource] +default_font = SubResource( 10 ) diff --git a/addons/gut/gui/RunAtCursor.gd b/addons/gut/gui/RunAtCursor.gd new file mode 100644 index 00000000..960414eb --- /dev/null +++ b/addons/gut/gui/RunAtCursor.gd @@ -0,0 +1,130 @@ +tool +extends Control + + +var ScriptTextEditors = load('res://addons/gut/gui/script_text_editor_controls.gd') + +onready var _ctrls = { + btn_script = $HBox/BtnRunScript, + btn_inner = $HBox/BtnRunInnerClass, + btn_method = $HBox/BtnRunMethod, + lbl_none = $HBox/LblNoneSelected, + arrow_1 = $HBox/Arrow1, + arrow_2 = $HBox/Arrow2 +} + +var _editors = null +var _script_editor = null +var _cur_editor = null +var _last_line = -1 +var _cur_script_path = null +var _last_info = null + +signal run_tests(what) + + +func _ready(): + _ctrls.lbl_none.visible = true + _ctrls.btn_script.visible = false + _ctrls.btn_inner.visible = false + _ctrls.btn_method.visible = false + + +func _set_editor(which): + _last_line = -1 + if(_cur_editor != null and _cur_editor.get_ref()): + _cur_editor.get_ref().disconnect('cursor_changed', self, '_on_cursor_changed') + + if(which != null): + _cur_editor = weakref(which) + which.connect('cursor_changed', self, '_on_cursor_changed', [which]) + + _last_line = which.cursor_get_line() + _last_info = _editors.get_line_info() + _update_buttons(_last_info) + + + +func _update_buttons(info): + _ctrls.lbl_none.visible = _cur_script_path == null + _ctrls.btn_script.visible = _cur_script_path != null + + _ctrls.btn_inner.visible = info.inner_class != null + _ctrls.arrow_1.visible = info.inner_class != null + _ctrls.btn_inner.text = str(info.inner_class) + _ctrls.btn_inner.hint_tooltip = str("Run all tests in Inner-Test-Class ", info.inner_class) + + _ctrls.btn_method.visible = info.test_method != null + _ctrls.arrow_2.visible = info.test_method != null + _ctrls.btn_method.text = str(info.test_method) + _ctrls.btn_method.hint_tooltip = str("Run test ", info.test_method) + + # The button's new size won't take effect until the next frame. + # This appears to be what was causing the button to not be clickable the + # first time. + call_deferred("_update_rect_size") + + +func _update_rect_size(): + rect_min_size.x = _ctrls.btn_method.rect_size.x + _ctrls.btn_method.rect_position.x + +func _on_cursor_changed(which): + if(which.cursor_get_line() != _last_line): + _last_line = which.cursor_get_line() + _last_info = _editors.get_line_info() + _update_buttons(_last_info) + + +func set_script_editor(value): + _script_editor = value + _editors = ScriptTextEditors.new(value) + + +func activate_for_script(path): + _ctrls.btn_script.visible = true + _ctrls.btn_script.text = path.get_file() + _ctrls.btn_script.hint_tooltip = str("Run all tests in script ", path) + _cur_script_path = path + _editors.refresh() + _set_editor(_editors.get_current_text_edit()) + + +func _on_BtnRunScript_pressed(): + var info = _last_info.duplicate() + info.script = _cur_script_path.get_file() + info.inner_class = null + info.test_method = null + emit_signal("run_tests", info) + + +func _on_BtnRunInnerClass_pressed(): + var info = _last_info.duplicate() + info.script = _cur_script_path.get_file() + info.test_method = null + emit_signal("run_tests", info) + + +func _on_BtnRunMethod_pressed(): + var info = _last_info.duplicate() + info.script = _cur_script_path.get_file() + emit_signal("run_tests", info) + + +func get_script_button(): + return _ctrls.btn_script + + +func get_inner_button(): + return _ctrls.btn_inner + + +func get_test_button(): + return _ctrls.btn_method + +# not used, thought was configurable but it's just the script prefix +func set_method_prefix(value): + _editors.set_method_prefix(value) + +# not used, thought was configurable but it's just the script prefix +func set_inner_class_prefix(value): + _editors.set_inner_class_prefix(value) diff --git a/addons/gut/gui/RunAtCursor.tscn b/addons/gut/gui/RunAtCursor.tscn new file mode 100644 index 00000000..b4662dfc --- /dev/null +++ b/addons/gut/gui/RunAtCursor.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/gut/gui/RunAtCursor.gd" type="Script" id=1] +[ext_resource path="res://addons/gut/gui/play.png" type="Texture" id=2] +[ext_resource path="res://addons/gut/gui/arrow.png" type="Texture" id=3] + +[node name="RunAtCursor" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_right = 1.0 +margin_bottom = -527.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HBox" type="HBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="LblNoneSelected" type="Label" parent="HBox"] +margin_top = 29.0 +margin_right = 50.0 +margin_bottom = 43.0 +text = "" + +[node name="BtnRunScript" type="Button" parent="HBox"] +visible = false +margin_left = 54.0 +margin_right = 140.0 +margin_bottom = 73.0 +text = "