diff options
| author | Sophia Pearson <codergal89@gmail.com> | 2022-09-05 20:35:53 +0200 |
|---|---|---|
| committer | Sophia Pearson <codergal89@gmail.com> | 2022-09-05 20:35:53 +0200 |
| commit | f20bd89dc4a7bf14a88b1effcaa1887b29314525 (patch) | |
| tree | d114787f68efd2a7d61d95fa9c84e8e5d69a7c11 /addons/gut | |
| parent | 1b477b62f8be8c546a35dbd1d2688ebf623c496f (diff) | |
| download | texty-f20bd89dc4a7bf14a88b1effcaa1887b29314525.tar.xz texty-f20bd89dc4a7bf14a88b1effcaa1887b29314525.zip | |
gui: split GUI into Terminal components
Diffstat (limited to 'addons/gut')
49 files changed, 1833 insertions, 345 deletions
diff --git a/addons/gut/GutScene.gd b/addons/gut/GutScene.gd index 251ab8f..2183a81 100644 --- a/addons/gut/GutScene.gd +++ b/addons/gut/GutScene.gd @@ -20,11 +20,13 @@ onready var _progress = { } onready var _summary = { control = $VBox/TitleBar/HBox/Summary, - failing = $VBox/TitleBar/HBox/Summary/Failing, - passing = $VBox/TitleBar/HBox/Summary/Passing, + failing = $VBox/TitleBar/HBox/Summary/Failing, # defunct? + passing = $VBox/TitleBar/HBox/Summary/Passing, # defunct? asserts = $VBox/TitleBar/HBox/Summary/AssertCount, - fail_count = 0, - pass_count = 0 + fail_count = 0, # defunct? + pass_count = 0, # defunct? + test_count = 0, + passing_test_count = 0 } onready var _extras = $ExtraOptions @@ -306,7 +308,8 @@ func _update_summary(): var total = _summary.fail_count + _summary.pass_count _summary.control.visible = !total == 0 - _summary.asserts.text = str('Failures ', _summary.fail_count, '/', total) + # this now shows tests but I didn't rename everything + _summary.asserts.text = str(_summary.passing_test_count, '/', _summary.test_count, ' tests passed') # #################### # Public # #################### @@ -397,6 +400,14 @@ func add_failing(amount=1): _summary.fail_count += amount _update_summary() +func add_test(passing): + if(!_summary): + return + _summary.test_count += 1 + if(passing): + _summary.passing_test_count += 1 + _update_summary() + func clear_summary(): _summary.fail_count = 0 _summary.pass_count = 0 @@ -473,7 +484,7 @@ func get_waiting_label(): func compact_mode(should): if(_compact_mode == should): return - + _compact_mode = should _text_box_container.visible = !should _nav.container.visible = !should @@ -483,14 +494,14 @@ func compact_mode(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() diff --git a/addons/gut/GutScene.tscn b/addons/gut/GutScene.tscn index a2ea3b0..90ae24f 100644 --- a/addons/gut/GutScene.tscn +++ b/addons/gut/GutScene.tscn @@ -49,9 +49,6 @@ rect_min_size = Vector2( 740, 300 ) theme = ExtResource( 7 ) custom_styles/panel = SubResource( 1 ) script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} [node name="UserFileViewer" parent="." instance=ExtResource( 6 )] margin_top = 388.0 diff --git a/addons/gut/UserFileViewer.gd b/addons/gut/UserFileViewer.gd index 3984dec..9713a94 100644 --- a/addons/gut/UserFileViewer.gd +++ b/addons/gut/UserFileViewer.gd @@ -32,7 +32,7 @@ func show_file(path): text = '<Empty File>' rtl.set_text(text) self.window_title = path - + func show_open(): self.popup_centered() $FileDialog.popup_centered() diff --git a/addons/gut/UserFileViewer.tscn b/addons/gut/UserFileViewer.tscn index 528d1cd..15481b6 100644 --- a/addons/gut/UserFileViewer.tscn +++ b/addons/gut/UserFileViewer.tscn @@ -26,8 +26,6 @@ resizable = true mode = 0 access = 1 show_hidden_files = true -current_dir = "user://" -current_path = "user://" __meta__ = { "_edit_use_anchors_": false } diff --git a/addons/gut/comparator.gd b/addons/gut/comparator.gd index ff03af8..b34ef44 100644 --- a/addons/gut/comparator.gd +++ b/addons/gut/comparator.gd @@ -65,8 +65,7 @@ func simple(v1, v2, missing_string=''): extra = str('. ', _cannot_comapre_text(v1, v2)) cmp_str = get_compare_symbol(result.are_equal) - if(typeof(v1) != TYPE_ARRAY): - result.summary = str(format_value(v1), ' ', cmp_str, ' ', format_value(v2), extra) + result.summary = str(format_value(v1), ' ', cmp_str, ' ', format_value(v2), extra) return result diff --git a/addons/gut/diff_tool.gd b/addons/gut/diff_tool.gd index daef7ed..9dbbd1c 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
\ No newline at end of file + return _value_2 diff --git a/addons/gut/double_templates/init_template.txt b/addons/gut/double_templates/init_template.txt new file mode 100644 index 0000000..8a0cb95 --- /dev/null +++ b/addons/gut/double_templates/init_template.txt @@ -0,0 +1,3 @@ +{func_decleration}{super_params}: + __gut_init() + __gut_spy('{method_name}', {param_array}) diff --git a/addons/gut/double_templates/script_template.txt b/addons/gut/double_templates/script_template.txt index b1e1d77..2071207 100644 --- a/addons/gut/double_templates/script_template.txt +++ b/addons/gut/double_templates/script_template.txt @@ -49,8 +49,7 @@ func __gut_default_val(method_name, p_index): else: return null - -func _init(): +func __gut_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 78becdd..ce865e5 100644 --- a/addons/gut/doubler.gd +++ b/addons/gut/doubler.gd @@ -61,13 +61,6 @@ class ScriptMethods: '_get_minimum_size', # Nonexistent function _get_minimum_size ] - # These methods should not be included in the double. - var _skip = [ - # There is an init in the template. There is also no real reason - # to include this method since it will always be called, it has no - # return value, and you cannot prevent super from being called. - '_init' - ] var built_ins = [] var local_methods = [] @@ -77,8 +70,6 @@ class ScriptMethods: return _blacklist.find(method_meta.name) != -1 func _add_name_if_does_not_have(method_name): - if(_skip.has(method_name)): - return false var should_add = _method_names.find(method_name) == -1 if(should_add): _method_names.append(method_name) @@ -394,7 +385,7 @@ func _stub_to_call_super(obj_info, method_name): _stubber.add_stub(params) -func _get_base_script_text(obj_info, override_path): +func _get_base_script_text(obj_info, override_path, script_methods): var path = obj_info.get_path() if(override_path != null): path = override_path @@ -431,8 +422,8 @@ func _get_base_script_text(obj_info, override_path): 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 base_script = _get_base_script_text(obj_info, override_path, script_methods) var super_name = "" var path = "" @@ -757,4 +748,4 @@ func set_make_files(make_files): set_output_dir(_output_dir) func get_method_maker(): - return _method_maker
\ No newline at end of file + return _method_maker diff --git a/addons/gut/fonts/OFL.txt b/addons/gut/fonts/OFL.txt index 5ca1911..3ed0152 100644 --- a/addons/gut/fonts/OFL.txt +++ b/addons/gut/fonts/OFL.txt @@ -19,7 +19,7 @@ with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, +fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The diff --git a/addons/gut/gui/GutBottomPanel.gd b/addons/gut/gui/GutBottomPanel.gd index 50a32d8..e7b5037 100644 --- a/addons/gut/gui/GutBottomPanel.gd +++ b/addons/gut/gui/GutBottomPanel.gd @@ -8,6 +8,7 @@ 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 ScriptTextEditors = load('res://addons/gut/gui/script_text_editor_controls.gd') var _interface = null; var _is_running = false; @@ -20,12 +21,20 @@ var _last_selected_path = null onready var _ctrls = { - output = $layout/RSplit/CResults/Output, + output = $layout/RSplit/CResults/Tabs/OutputText.get_rich_text_edit(), + output_ctrl = $layout/RSplit/CResults/Tabs/OutputText, run_button = $layout/ControlBar/RunAll, + shortcuts_button = $layout/ControlBar/Shortcuts, + + settings_button = $layout/ControlBar/Settings, + run_results_button = $layout/ControlBar/RunResultsBtn, + output_button = $layout/ControlBar/OutputBtn, + settings = $layout/RSplit/sc/Settings, shortcut_dialog = $BottomPanelShortcuts, light = $layout/RSplit/CResults/ControlBar/Light, results = { + bar = $layout/RSplit/CResults/ControlBar, passing = $layout/RSplit/CResults/ControlBar/Passing/value, failing = $layout/RSplit/CResults/ControlBar/Failing/value, pending = $layout/RSplit/CResults/ControlBar/Pending/value, @@ -33,7 +42,8 @@ onready var _ctrls = { warnings = $layout/RSplit/CResults/ControlBar/Warnings/value, orphans = $layout/RSplit/CResults/ControlBar/Orphans/value }, - run_at_cursor = $layout/ControlBar/RunAtCursor + run_at_cursor = $layout/ControlBar/RunAtCursor, + run_results = $layout/RSplit/CResults/Tabs/RunResults } @@ -42,17 +52,51 @@ func _init(): func _ready(): + _ctrls.results.bar.connect('draw', self, '_on_results_bar_draw', [_ctrls.results.bar]) + hide_settings(!_ctrls.settings_button.pressed) _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) + + _apply_options_to_controls() + + _ctrls.shortcuts_button.icon = get_icon('ShortCut', 'EditorIcons') + _ctrls.settings_button.icon = get_icon('Tools', 'EditorIcons') + _ctrls.run_results_button.icon = get_icon('AnimationTrackGroup', 'EditorIcons') # Tree + _ctrls.output_button.icon = get_icon('Font', 'EditorIcons') + + _ctrls.run_results.set_output_control(_ctrls.output_ctrl) + _ctrls.run_results.set_font( + _gut_config.options.panel_options.font_name, + _gut_config.options.panel_options.font_size) + + var check_import = load('res://addons/gut/images/red.png') + if(check_import == null): + _ctrls.run_results.add_centered_text("GUT got some new images that are not imported yet. Please restart Godot.") + print('GUT got some new images that are not imported yet. Please restart Godot.') + else: + _ctrls.run_results.add_centered_text("Let's run some tests!") + + +func _apply_options_to_controls(): + hide_settings(_gut_config.options.panel_options.hide_settings) + hide_result_tree(_gut_config.options.panel_options.hide_result_tree) + hide_output_text(_gut_config.options.panel_options.hide_output_text) + + _ctrls.output_ctrl.set_use_colors(_gut_config.options.panel_options.use_colors) + _ctrls.output_ctrl.set_all_fonts(_gut_config.options.panel_options.font_name) + _ctrls.output_ctrl.set_font_size(_gut_config.options.panel_options.font_size) + + _ctrls.run_results.set_font( + _gut_config.options.panel_options.font_name, + _gut_config.options.panel_options.font_size) + _ctrls.run_results.set_show_orphans(!_gut_config.options.hide_orphans) func _process(delta): if(_is_running): if(!_interface.is_playing_scene()): _is_running = false - _ctrls.output.add_text("\ndone") + _ctrls.output_ctrl.add_text("\ndone") load_result_output() _gut_plugin.make_bottom_panel_item_visible(self) @@ -65,41 +109,6 @@ func load_shortcuts(): _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'): @@ -108,27 +117,28 @@ func _is_test_script(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" + _ctrls.output_ctrl.clear() + var text = "Cannot run tests, you have a configuration error:\n" for e in errs: text += str('* ', e, "\n") - text += "[right]Check your settings here ----->[/right]" - _ctrls.output.bbcode_text = text + text += "Check your settings ----->" + _ctrls.output_ctrl.add_text(text) + hide_output_text(false) + hide_settings(false) + + +func _save_config(): + _gut_config.options = _gut_config_gui.get_options(_gut_config.options) + _gut_config.options.panel_options.hide_settings = !_ctrls.settings_button.pressed + _gut_config.options.panel_options.hide_result_tree = !_ctrls.run_results_button.pressed + _gut_config.options.panel_options.hide_output_text = !_ctrls.output_button.pressed + _gut_config.options.panel_options.use_colors = _ctrls.output_ctrl.get_use_colors() + + 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; func _run_tests(): @@ -138,22 +148,16 @@ func _run_tests(): 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) + _save_config() + _apply_options_to_controls() - 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() + _ctrls.output_ctrl.clear() + _ctrls.run_results.clear() + _ctrls.run_results.add_centered_text('Running...') - _update_last_run_label() _interface.play_custom_scene('res://addons/gut/gui/GutRunner.tscn') - _is_running = true - _ctrls.output.add_text('running...') + _ctrls.output_ctrl.add_text('Running...') func _apply_shortcuts(): @@ -180,27 +184,24 @@ func _run_all(): # --------------- # Events # --------------- +func _on_results_bar_draw(bar): + bar.draw_rect(Rect2(Vector2(0, 0), bar.rect_size), Color(0, 0, 0, .2)) + + +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_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() @@ -210,11 +211,6 @@ func _on_BottomPanelShortcuts_popup_hide(): _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 @@ -223,19 +219,64 @@ func _on_RunAtCursor_run_tests(what): _run_tests() +func _on_Settings_pressed(): + hide_settings(!_ctrls.settings_button.pressed) + _save_config() + + +func _on_OutputBtn_pressed(): + hide_output_text(!_ctrls.output_button.pressed) + _save_config() + + +func _on_RunResultsBtn_pressed(): + hide_result_tree(! _ctrls.run_results_button.pressed) + _save_config() + + +# Currently not used, but will be when I figure out how to put +# colors into the text results +func _on_UseColors_pressed(): + pass + # --------------- # Public # --------------- +func hide_result_tree(should): + _ctrls.run_results.visible = !should + _ctrls.run_results_button.pressed = !should + + +func hide_settings(should): + var s_scroll = _ctrls.settings.get_parent() + s_scroll.visible = !should + + # collapse only collapses the first control, so we move + # settings around to be the collapsed one + if(should): + s_scroll.get_parent().move_child(s_scroll, 0) + else: + s_scroll.get_parent().move_child(s_scroll, 1) + + $layout/RSplit.collapsed = should + _ctrls.settings_button.pressed = !should + + +func hide_output_text(should): + $layout/RSplit/CResults/Tabs/OutputText.visible = !should + _ctrls.output_button.pressed = !should + 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) + _ctrls.output_ctrl.load_file(RESULT_FILE) var summary = get_file_as_text(RESULT_JSON) var results = JSON.parse(summary) if(results.error != OK): return + + _ctrls.run_results.load_json_results(results.result) + var summary_json = results.result['test_scripts']['props'] _ctrls.results.passing.text = str(summary_json.passing) _ctrls.results.passing.get_parent().visible = true @@ -263,10 +304,10 @@ func load_result_output(): _light_color = Color(1, 1, 0, .75) else: _light_color = Color(0, 1, 0, .75) + _ctrls.light.visible = true _ctrls.light.update() - func set_current_script(script): if(script): if(_is_test_script(script)): @@ -278,7 +319,11 @@ func set_current_script(script): 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()) + + var ste = ScriptTextEditors.new(_interface.get_script_editor()) + _ctrls.run_results.set_interface(_interface) + _ctrls.run_results.set_script_text_editors(ste) + _ctrls.run_at_cursor.set_script_text_editors(ste) set_current_script(_interface.get_script_editor().get_current_script()) @@ -323,4 +368,3 @@ func nvl(value, if_null): else: return value - diff --git a/addons/gut/gui/GutBottomPanel.tscn b/addons/gut/gui/GutBottomPanel.tscn index 0bb7fc3..7ec6649 100644 --- a/addons/gut/gui/GutBottomPanel.tscn +++ b/addons/gut/gui/GutBottomPanel.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=16 format=2] +[gd_scene load_steps=11 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] +[ext_resource path="res://addons/gut/gui/RunResults.tscn" type="PackedScene" id=5] +[ext_resource path="res://addons/gut/gui/OutputText.tscn" type="PackedScene" id=6] [sub_resource type="InputEventKey" id=8] control = true @@ -12,35 +14,20 @@ 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="Image" id=10] +data = { +"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), +"format": "LumAlpha8", +"height": 16, +"mipmaps": false, +"width": 16 +} -[sub_resource type="DynamicFont" id=17] -size = 30 -font_data = SubResource( 16 ) +[sub_resource type="ImageTexture" id=2] +flags = 4 +flags = 4 +image = SubResource( 10 ) +size = Vector2( 16, 16 ) [node name="GutBottomPanel" type="Control"] anchor_left = -0.0025866 @@ -53,9 +40,6 @@ 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 @@ -89,16 +73,16 @@ 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 +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 +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 +You can assign keyboard shortcuts for these buttons using the \"shortcuts\" button in the GUT panel." mouse_filter = 1 text = "Current: " @@ -107,39 +91,61 @@ text = "Current: " anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 217.0 -margin_right = 456.0 +margin_right = 548.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_left = 552.0 +margin_right = 883.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 +[node name="Sep1" type="ColorRect" parent="layout/ControlBar"] +margin_left = 887.0 +margin_right = 889.0 margin_bottom = 40.0 -custom_styles/normal = SubResource( 5 ) -__meta__ = { -"_edit_use_anchors_": false -} +rect_min_size = Vector2( 2, 0 ) -[node name="CenterContainer" type="CenterContainer" parent="layout/ControlBar"] -margin_left = 707.0 -margin_right = 946.0 +[node name="RunResultsBtn" type="ToolButton" parent="layout/ControlBar"] +margin_left = 893.0 +margin_right = 921.0 margin_bottom = 40.0 -size_flags_horizontal = 3 +hint_tooltip = "Show/Hide Results Tree Panel." +toggle_mode = true +pressed = true +icon = SubResource( 2 ) + +[node name="OutputBtn" type="ToolButton" parent="layout/ControlBar"] +margin_left = 925.0 +margin_right = 953.0 +margin_bottom = 40.0 +hint_tooltip = "Show/Hide Output Panel." +toggle_mode = true +pressed = true +icon = SubResource( 2 ) + +[node name="Settings" type="ToolButton" parent="layout/ControlBar"] +margin_left = 957.0 +margin_right = 985.0 +margin_bottom = 40.0 +hint_tooltip = "Show/Hide Settings Panel." +toggle_mode = true +icon = SubResource( 2 ) -[node name="Shortcuts" type="Button" parent="layout/ControlBar"] -margin_left = 950.0 -margin_right = 1022.0 +[node name="Sep2" type="ColorRect" parent="layout/ControlBar"] +margin_left = 989.0 +margin_right = 991.0 +margin_bottom = 40.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="Shortcuts" type="ToolButton" parent="layout/ControlBar"] +margin_left = 995.0 +margin_right = 1023.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" +icon = SubResource( 2 ) [node name="RSplit" type="HSplitContainer" parent="layout"] margin_top = 44.0 @@ -147,19 +153,36 @@ margin_right = 1023.0 margin_bottom = 599.0 size_flags_horizontal = 3 size_flags_vertical = 3 +collapsed = true + +[node name="sc" type="ScrollContainer" parent="layout/RSplit"] +visible = false +margin_left = 593.0 +margin_right = 1093.0 +margin_bottom = 555.0 +rect_min_size = Vector2( 500, 0 ) +mouse_filter = 1 +size_flags_vertical = 3 + +[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"] +margin_right = 500.0 +margin_bottom = 908.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 [node name="CResults" type="VBoxContainer" parent="layout/RSplit"] -margin_right = 611.0 +margin_right = 1023.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_right = 1023.0 margin_bottom = 35.0 rect_min_size = Vector2( 0, 35 ) [node name="Light" type="Control" parent="layout/RSplit/CResults/ControlBar"] +visible = false margin_right = 30.0 margin_bottom = 35.0 rect_min_size = Vector2( 30, 30 ) @@ -319,52 +342,28 @@ margin_bottom = 24.0 text = "---" [node name="CenterContainer" type="CenterContainer" parent="layout/RSplit/CResults/ControlBar"] -margin_left = 34.0 -margin_right = 488.0 +margin_right = 1023.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"] +[node name="Tabs" type="HSplitContainer" 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_horizontal = 3 size_flags_vertical = 3 -[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"] -margin_right = 388.0 -margin_bottom = 862.0 +[node name="RunResults" parent="layout/RSplit/CResults/Tabs" instance=ExtResource( 5 )] +margin_right = 505.0 +margin_bottom = 516.0 size_flags_horizontal = 3 size_flags_vertical = 3 +[node name="OutputText" parent="layout/RSplit/CResults/Tabs" instance=ExtResource( 6 )] +margin_left = 517.0 +margin_right = 1023.0 +margin_bottom = 516.0 + [node name="BottomPanelShortcuts" parent="." instance=ExtResource( 2 )] visible = false anchor_left = -0.000517324 @@ -378,9 +377,9 @@ 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/RunResultsBtn" to="." method="_on_RunResultsBtn_pressed"] +[connection signal="pressed" from="layout/ControlBar/OutputBtn" to="." method="_on_OutputBtn_pressed"] +[connection signal="pressed" from="layout/ControlBar/Settings" to="." method="_on_Settings_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 index b2b16f2..608fc26 100644 --- a/addons/gut/gui/GutRunner.gd +++ b/addons/gut/gui/GutRunner.gd @@ -22,8 +22,7 @@ onready var _gut_layer = $GutLayer func _ready(): if(_gut_config == null): _gut_config = GutConfig.new() - _gut_config.load_options(RUNNER_JSON_PATH) - + _gut_config.load_panel_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 @@ -55,9 +54,7 @@ func run_tests(): 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 content = _gut.get_logger().get_gui_bbcode() var f = File.new() var result = f.open(RESULT_FILE, f.WRITE) @@ -68,7 +65,7 @@ func _write_results(): print('ERROR Could not save bbcode, result = ', result) var exporter = ResultExporter.new() - var f_result = exporter.write_summary_file(_gut, RESULT_JSON) + var f_result = exporter.write_json_file(_gut, RESULT_JSON) _wrote_results = true diff --git a/addons/gut/gui/OutputText.gd b/addons/gut/gui/OutputText.gd new file mode 100644 index 0000000..e5cf2b6 --- /dev/null +++ b/addons/gut/gui/OutputText.gd @@ -0,0 +1,291 @@ +extends VBoxContainer +tool + +class SearchResults: + const L = TextEdit.SEARCH_RESULT_LINE + const C = TextEdit.SEARCH_RESULT_COLUMN + + var positions = [] + var te = null + var _last_term = '' + + func _search_te(text, start_position, flags=0): + var start_pos = start_position + if(start_pos[L] < 0 or start_pos[L] > te.get_line_count()): + start_pos[L] = 0 + if(start_pos[C] < 0): + start_pos[L] = 0 + + var result = te.search(text, flags, start_pos[L], start_pos[C]) + if(result.size() == 2 and result[L] == start_position[L] and + result[C] == start_position[C] and text == _last_term): + if(flags == TextEdit.SEARCH_BACKWARDS): + result[C] -= 1 + else: + result[C] += 1 + result = _search_te(text, result, flags) + elif(result.size() == 2): + te.scroll_vertical = result[L] + te.select(result[L], result[C], result[L], result[C] + text.length()) + te.cursor_set_column(result[C]) + te.cursor_set_line(result[L]) + te.center_viewport_to_cursor() + + _last_term = text + te.center_viewport_to_cursor() + return result + + func _cursor_to_pos(): + var to_return = [0, 0] + to_return[L] = te.cursor_get_line() + to_return[C] = te.cursor_get_column() + return to_return + + func find_next(term): + return _search_te(term, _cursor_to_pos()) + + func find_prev(term): + var new_pos = _search_te(term, _cursor_to_pos(), TextEdit.SEARCH_BACKWARDS) + return new_pos + + func get_next_pos(): + pass + + func get_prev_pos(): + pass + + func clear(): + pass + + func find_all(text): + var c_pos = [0, 0] + var found = true + var last_pos = [0, 0] + positions.clear() + + while(found): + c_pos = te.search(text, 0, c_pos[L], c_pos[C]) + + if(c_pos.size() > 0 and + (c_pos[L] > last_pos[L] or + (c_pos[L] == last_pos[L] and c_pos[C] > last_pos[C]))): + positions.append([c_pos[L], c_pos[C]]) + c_pos[C] += 1 + last_pos = c_pos + else: + found = false + + + +onready var _ctrls = { + output = $Output, + + copy_button = $Toolbar/CopyButton, + use_colors = $Toolbar/UseColors, + clear_button = $Toolbar/ClearButton, + word_wrap = $Toolbar/WordWrap, + show_search = $Toolbar/ShowSearch, + + search_bar = { + bar = $Search, + search_term = $Search/SearchTerm, + } +} +var _sr = SearchResults.new() + +func _test_running_setup(): + _ctrls.use_colors.text = 'use colors' + _ctrls.show_search.text = 'search' + _ctrls.word_wrap.text = 'ww' + + set_all_fonts("CourierPrime") + set_font_size(20) + + load_file('user://.gut_editor.bbcode') + + +func _ready(): + _sr.te = _ctrls.output + _ctrls.use_colors.icon = get_icon('RichTextEffect', 'EditorIcons') + _ctrls.show_search.icon = get_icon('Search', 'EditorIcons') + _ctrls.word_wrap.icon = get_icon('Loop', 'EditorIcons') + + _setup_colors() + if(get_parent() == get_tree().root): + _test_running_setup() + + +# ------------------ +# Private +# ------------------ +func _setup_colors(): + _ctrls.output.clear_colors() + var keywords = [ + ['Failed', Color.red], + ['Passed', Color.green], + ['Pending', Color.yellow], + ['Orphans', Color.yellow], + ['WARNING', Color.yellow], + ['ERROR', Color.red] + ] + + for keyword in keywords: + _ctrls.output.add_keyword_color(keyword[0], keyword[1]) + + var f_color = _ctrls.output.get_color("font_color") + _ctrls.output.add_color_override("font_color_readonly", f_color) + _ctrls.output.add_color_override("function_color", f_color) + _ctrls.output.add_color_override("member_variable_color", f_color) + _ctrls.output.update() + + +func _set_font(font_name, custom_name): + var rtl = _ctrls.output + 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) + + +# ------------------ +# Events +# ------------------ +func _on_CopyButton_pressed(): + copy_to_clipboard() + + +func _on_UseColors_pressed(): + _ctrls.output.syntax_highlighting = _ctrls.use_colors.pressed + + +func _on_ClearButton_pressed(): + clear() + + +func _on_ShowSearch_pressed(): + show_search(_ctrls.show_search.pressed) + + +func _on_SearchTerm_focus_entered(): + _ctrls.search_bar.search_term.call_deferred('select_all') + +func _on_SearchNext_pressed(): + _sr.find_next(_ctrls.search_bar.search_term.text) + + +func _on_SearchPrev_pressed(): + _sr.find_prev(_ctrls.search_bar.search_term.text) + + +func _on_SearchTerm_text_changed(new_text): + if(new_text == ''): + _ctrls.output.deselect() + else: + _sr.find_next(new_text) + + +func _on_SearchTerm_text_entered(new_text): + if(Input.is_physical_key_pressed(KEY_SHIFT)): + _sr.find_prev(new_text) + else: + _sr.find_next(new_text) + + +func _on_SearchTerm_gui_input(event): + if(event is InputEventKey and !event.pressed and event.scancode == KEY_ESCAPE): + show_search(false) + +func _on_WordWrap_pressed(): + _ctrls.output.wrap_enabled = _ctrls.word_wrap.pressed + _ctrls.output.update() + +# ------------------ +# Public +# ------------------ +func show_search(should): + _ctrls.search_bar.bar.visible = should + if(should): + _ctrls.search_bar.search_term.grab_focus() + _ctrls.search_bar.search_term.select_all() + _ctrls.show_search.pressed = should + + +func search(text, start_pos, highlight=true): + return _sr.find_next(text) + + +func copy_to_clipboard(): + var selected = _ctrls.output.get_selection_text() + if(selected != ''): + OS.clipboard = selected + else: + OS.clipboard = _ctrls.output.text + + +func clear(): + _ctrls.output.text = '' + + +func set_all_fonts(base_name): + if(base_name == 'Default'): + _set_font(null, 'font') +# _set_font(null, 'normal_font') +# _set_font(null, 'bold_font') +# _set_font(null, 'italics_font') +# _set_font(null, 'bold_italics_font') + else: + _set_font(base_name + '-Regular', 'font') +# _set_font(base_name + '-Regular', 'normal_font') +# _set_font(base_name + '-Bold', 'bold_font') +# _set_font(base_name + '-Italic', 'italics_font') +# _set_font(base_name + '-BoldItalic', 'bold_italics_font') + + +func set_font_size(new_size): + var rtl = _ctrls.output + if(rtl.get('custom_fonts/font') != null): + rtl.get('custom_fonts/font').size = new_size +# 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 set_use_colors(value): + pass + + +func get_use_colors(): + return false; + + +func get_rich_text_edit(): + return _ctrls.output + + +func load_file(path): + var f = File.new() + var result = f.open(path, f.READ) + if(result != OK): + return + + var t = f.get_as_text() + f.close() + _ctrls.output.text = t + _ctrls.output.scroll_vertical = _ctrls.output.get_line_count() + _ctrls.output.set_deferred('scroll_vertical', _ctrls.output.get_line_count()) + + +func add_text(text): + if(is_inside_tree()): + _ctrls.output.text += text + + +func scroll_to_line(line): + _ctrls.output.scroll_vertical = line + _ctrls.output.cursor_set_line(line) diff --git a/addons/gut/gui/OutputText.tscn b/addons/gut/gui/OutputText.tscn new file mode 100644 index 0000000..d7c693a --- /dev/null +++ b/addons/gut/gui/OutputText.tscn @@ -0,0 +1,123 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/gut/gui/OutputText.gd" type="Script" id=1] + +[sub_resource type="Image" id=3] +data = { +"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), +"format": "LumAlpha8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id=2] +flags = 4 +flags = 4 +image = SubResource( 3 ) +size = Vector2( 16, 16 ) + +[node name="OutputText" type="VBoxContainer"] +margin_right = 862.0 +margin_bottom = 523.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) + +[node name="Toolbar" type="HBoxContainer" parent="."] +margin_right = 862.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 + +[node name="ShowSearch" type="ToolButton" parent="Toolbar"] +margin_right = 28.0 +margin_bottom = 24.0 +toggle_mode = true +icon = SubResource( 2 ) + +[node name="UseColors" type="ToolButton" parent="Toolbar"] +margin_left = 32.0 +margin_right = 60.0 +margin_bottom = 24.0 +hint_tooltip = "Colorize output. + It's not the same as everywhere else (long story), + but it is better than nothing." +toggle_mode = true +pressed = true +icon = SubResource( 2 ) + +[node name="WordWrap" type="ToolButton" parent="Toolbar"] +margin_left = 64.0 +margin_right = 92.0 +margin_bottom = 24.0 +hint_tooltip = "Word wrap" +toggle_mode = true +icon = SubResource( 2 ) + +[node name="CenterContainer" type="CenterContainer" parent="Toolbar"] +margin_left = 96.0 +margin_right = 743.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 + +[node name="CopyButton" type="Button" parent="Toolbar"] +margin_left = 747.0 +margin_right = 798.0 +margin_bottom = 24.0 +hint_tooltip = "Copy to clipboard" +text = " Copy " + +[node name="ClearButton" type="Button" parent="Toolbar"] +margin_left = 802.0 +margin_right = 862.0 +margin_bottom = 24.0 +text = " Clear " + +[node name="Output" type="TextEdit" parent="."] +margin_top = 28.0 +margin_right = 862.0 +margin_bottom = 523.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +readonly = true +highlight_current_line = true +syntax_highlighting = true +show_line_numbers = true +smooth_scrolling = true + +[node name="Search" type="HBoxContainer" parent="."] +visible = false +margin_top = 499.0 +margin_right = 862.0 +margin_bottom = 523.0 + +[node name="SearchTerm" type="LineEdit" parent="Search"] +margin_right = 804.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 + +[node name="SearchNext" type="Button" parent="Search"] +margin_left = 808.0 +margin_right = 862.0 +margin_bottom = 24.0 +hint_tooltip = "Find next (enter)" +text = "Next" + +[node name="SearchPrev" type="Button" parent="Search"] +margin_left = 808.0 +margin_right = 820.0 +margin_bottom = 20.0 +hint_tooltip = "Find previous (shift + enter)" +text = "Prev" + +[connection signal="pressed" from="Toolbar/ShowSearch" to="." method="_on_ShowSearch_pressed"] +[connection signal="pressed" from="Toolbar/UseColors" to="." method="_on_UseColors_pressed"] +[connection signal="pressed" from="Toolbar/WordWrap" to="." method="_on_WordWrap_pressed"] +[connection signal="pressed" from="Toolbar/CopyButton" to="." method="_on_CopyButton_pressed"] +[connection signal="pressed" from="Toolbar/ClearButton" to="." method="_on_ClearButton_pressed"] +[connection signal="focus_entered" from="Search/SearchTerm" to="." method="_on_SearchTerm_focus_entered"] +[connection signal="gui_input" from="Search/SearchTerm" to="." method="_on_SearchTerm_gui_input"] +[connection signal="text_changed" from="Search/SearchTerm" to="." method="_on_SearchTerm_text_changed"] +[connection signal="text_entered" from="Search/SearchTerm" to="." method="_on_SearchTerm_text_entered"] +[connection signal="pressed" from="Search/SearchNext" to="." method="_on_SearchNext_pressed"] +[connection signal="pressed" from="Search/SearchPrev" to="." method="_on_SearchPrev_pressed"] diff --git a/addons/gut/gui/RunAtCursor.gd b/addons/gut/gui/RunAtCursor.gd index 960414e..dc83c3e 100644 --- a/addons/gut/gui/RunAtCursor.gd +++ b/addons/gut/gui/RunAtCursor.gd @@ -14,7 +14,6 @@ onready var _ctrls = { } var _editors = null -var _script_editor = null var _cur_editor = null var _last_line = -1 var _cur_script_path = null @@ -29,7 +28,9 @@ func _ready(): _ctrls.btn_inner.visible = false _ctrls.btn_method.visible = false - +# ---------------- +# Private +# ---------------- func _set_editor(which): _last_line = -1 if(_cur_editor != null and _cur_editor.get_ref()): @@ -44,7 +45,6 @@ func _set_editor(which): _update_buttons(_last_info) - func _update_buttons(info): _ctrls.lbl_none.visible = _cur_script_path == null _ctrls.btn_script.visible = _cur_script_path != null @@ -58,16 +58,19 @@ func _update_buttons(info): _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. + + # 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 +# ---------------- +# Events +# ---------------- func _on_cursor_changed(which): if(which.cursor_get_line() != _last_line): _last_line = which.cursor_get_line() @@ -75,20 +78,6 @@ func _on_cursor_changed(which): _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() @@ -110,6 +99,22 @@ func _on_BtnRunMethod_pressed(): emit_signal("run_tests", info) +# ---------------- +# Public +# ---------------- +func set_script_text_editors(value): + _editors = 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 get_script_button(): return _ctrls.btn_script @@ -121,10 +126,28 @@ func get_inner_button(): 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) + + +# Mashed this function in here b/c it has _editors. Probably should be +# somewhere else (possibly in script_text_editor_controls). +func search_current_editor_for_text(txt): + var te = _editors.get_current_text_edit() + var result = te.search(txt, 0, 0, 0) + var to_return = -1 + + if result.size() > 0: + to_return = result[TextEdit.SEARCH_RESULT_LINE] + + return to_return + + + diff --git a/addons/gut/gui/RunResults.gd b/addons/gut/gui/RunResults.gd new file mode 100644 index 0000000..a36d10f --- /dev/null +++ b/addons/gut/gui/RunResults.gd @@ -0,0 +1,509 @@ +extends Control +tool + +var _interface = null +var _utils = load('res://addons/gut/utils.gd').new() +var _hide_passing = true +var _font = null +var _font_size = null +var _root = null +var _max_icon_width = 10 +var _editors = null # script_text_editor_controls.gd +var _show_orphans = true +var _output_control = null + +const _col_1_bg_color = Color(0, 0, 0, .1) + +var _icons = { + red = load('res://addons/gut/images/red.png'), + green = load('res://addons/gut/images/green.png'), + yellow = load('res://addons/gut/images/yellow.png'), +} + +signal search_for_text(text) + +onready var _ctrls = { + tree = $VBox/Output/Scroll/Tree, + lbl_overlay = $VBox/Output/OverlayMessage, + chk_hide_passing = $VBox/Toolbar/HidePassing, + toolbar = { + toolbar = $VBox/Toolbar, + collapse = $VBox/Toolbar/Collapse, + collapse_all = $VBox/Toolbar/CollapseAll, + expand = $VBox/Toolbar/Expand, + expand_all = $VBox/Toolbar/ExpandAll, + hide_passing = $VBox/Toolbar/HidePassing, + show_script = $VBox/Toolbar/ShowScript, + scroll_output = $VBox/Toolbar/ScrollOutput + } +} + +func _test_running_setup(): + _hide_passing = true + _show_orphans = true + var _gut_config = load('res://addons/gut/gut_config.gd').new() + _gut_config.load_panel_options('res://.gut_editor_config.json') + set_font( + _gut_config.options.panel_options.font_name, + _gut_config.options.panel_options.font_size) + + _ctrls.toolbar.hide_passing.text = '[hp]' + load_json_file('user://.gut_editor.json') + + +func _set_toolbutton_icon(btn, icon_name, text): + if(Engine.editor_hint): + btn.icon = get_icon(icon_name, 'EditorIcons') + else: + btn.text = str('[', text, ']') + + +func _ready(): + var f = $FontSampler.get_font("font") + var s_size = f.get_string_size("000 of 000 passed") + _root = _ctrls.tree.create_item() + _ctrls.tree.set_hide_root(true) + _ctrls.tree.columns = 2 + _ctrls.tree.set_column_expand(0, true) + _ctrls.tree.set_column_expand(1, false) + _ctrls.tree.set_column_min_width(1, s_size.x) + + _set_toolbutton_icon(_ctrls.toolbar.collapse, 'CollapseTree', 'c') + _set_toolbutton_icon(_ctrls.toolbar.collapse_all, 'CollapseTree', 'c') + _set_toolbutton_icon(_ctrls.toolbar.expand, 'ExpandTree', 'e') + _set_toolbutton_icon(_ctrls.toolbar.expand_all, 'ExpandTree', 'e') + _set_toolbutton_icon(_ctrls.toolbar.show_script, 'Script', 'ss') + _set_toolbutton_icon(_ctrls.toolbar.scroll_output, 'Font', 'so') + + _ctrls.toolbar.hide_passing.set('custom_icons/checked', get_icon('GuiVisibilityHidden', 'EditorIcons')) + _ctrls.toolbar.hide_passing.set('custom_icons/unchecked', get_icon('GuiVisibilityVisible', 'EditorIcons')) + + if(get_parent() == get_tree().root): + _test_running_setup() + + call_deferred('_update_min_width') + +func _update_min_width(): + rect_min_size.x = _ctrls.toolbar.toolbar.rect_size.x + +func _open_file(path, line_number): + if(_interface == null): + print('Too soon, wait a bit and try again.') + return + + var r = load(path) + if(line_number != -1): + _interface.edit_script(r, line_number) + else: + _interface.edit_script(r) + + if(_ctrls.toolbar.show_script.pressed): + _interface.set_main_screen_editor('Script') + + +func _add_script_tree_item(script_path, script_json): + var path_info = _get_path_and_inner_class_name_from_test_path(script_path) + # print('* adding script ', path_info) + var item_text = script_path + var parent = _root + + if(path_info.inner_class != ''): + parent = _find_script_item_with_path(path_info.path) + item_text = path_info.inner_class + if(parent == null): + parent = _add_script_tree_item(path_info.path, {}) + + var item = _ctrls.tree.create_item(parent) + item.set_text(0, item_text) + var meta = { + "type":"script", + "path":path_info.path, + "inner_class":path_info.inner_class, + "json":script_json} + item.set_metadata(0, meta) + item.set_custom_bg_color(1, _col_1_bg_color) + + return item + + +func _add_assert_item(text, icon, parent_item): + # print(' * adding assert') + var assert_item = _ctrls.tree.create_item(parent_item) + assert_item.set_icon_max_width(0, _max_icon_width) + assert_item.set_text(0, text) + assert_item.set_metadata(0, {"type":"assert"}) + assert_item.set_icon(0, icon) + assert_item.set_custom_bg_color(1, _col_1_bg_color) + + return assert_item + + +func _add_test_tree_item(test_name, test_json, script_item): + # print(' * adding test ', test_name) + var no_orphans_to_show = !_show_orphans or (_show_orphans and test_json.orphans == 0) + if(_hide_passing and test_json['status'] == 'pass' and no_orphans_to_show): + return + + var item = _ctrls.tree.create_item(script_item) + var status = test_json['status'] + var meta = {"type":"test", "json":test_json} + + item.set_text(0, test_name) + item.set_text(1, status) + item.set_text_align(1, TreeItem.ALIGN_RIGHT) + item.set_custom_bg_color(1, _col_1_bg_color) + + item.set_metadata(0, meta) + item.set_icon_max_width(0, _max_icon_width) + + var orphan_text = 'orphans' + if(test_json.orphans == 1): + orphan_text = 'orphan' + orphan_text = str(test_json.orphans, ' ', orphan_text) + + + if(status == 'pass' and no_orphans_to_show): + item.set_icon(0, _icons.green) + elif(status == 'pass' and !no_orphans_to_show): + item.set_icon(0, _icons.yellow) + item.set_text(1, orphan_text) + elif(status == 'fail'): + item.set_icon(0, _icons.red) + else: + item.set_icon(0, _icons.yellow) + + if(!_hide_passing): + for passing in test_json.passing: + _add_assert_item('pass: ' + passing, _icons.green, item) + + for failure in test_json.failing: + _add_assert_item("fail: " + failure.replace("\n", ''), _icons.red, item) + + for pending in test_json.pending: + _add_assert_item("pending: " + pending.replace("\n", ''), _icons.yellow, item) + + if(status != 'pass' and !no_orphans_to_show): + _add_assert_item(orphan_text, _icons.yellow, item) + + return item + + +func _load_result_tree(j): + var scripts = j['test_scripts']['scripts'] + var script_keys = scripts.keys() + # if we made it here, the json is valid and we did something, otherwise the + # 'nothing to see here' should be visible. + clear_centered_text() + + var _last_script_item = null + for key in script_keys: + var tests = scripts[key]['tests'] + var test_keys = tests.keys() + var s_item = _add_script_tree_item(key, scripts[key]) + var bad_count = 0 + + for test_key in test_keys: + var t_item = _add_test_tree_item(test_key, tests[test_key], s_item) + if(tests[test_key].status != 'pass'): + bad_count += 1 + elif(t_item != null): + t_item.collapsed = true + + # get_children returns the first child or null. its a dumb name. + if(s_item.get_children() == null): + # var m = s_item.get_metadata(0) + # print('!! Deleting ', m.path, ' ', m.inner_class) + s_item.free() + else: + var total_text = str(test_keys.size(), ' passed') + s_item.set_text_align(1, s_item.ALIGN_LEFT) + if(bad_count == 0): + s_item.collapsed = true + else: + total_text = str(test_keys.size() - bad_count, ' of ', test_keys.size(), ' passed') + s_item.set_text(1, total_text) + + _free_childless_scripts() + _show_all_passed() + + +func _free_childless_scripts(): + var item = _root.get_children() + while(item != null): + var next_item = item.get_next() + if(item.get_children() == null): + item.free() + item = next_item + + +func _find_script_item_with_path(path): + var item = _root.get_children() + var to_return = null + + while(item != null and to_return == null): + if(item.get_metadata(0).path == path): + to_return = item + else: + item = item.get_next() + + return to_return + + +func _get_line_number_from_assert_msg(msg): + var line = -1 + if(msg.find('at line') > 0): + line = int(msg.split("at line")[-1].split(" ")[-1]) + return line + + +func _get_path_and_inner_class_name_from_test_path(path): + var to_return = { + path = '', + inner_class = '' + } + + to_return.path = path + if !path.ends_with('.gd'): + var loc = path.find('.gd') + to_return.inner_class = path.split('.')[-1] + to_return.path = path.substr(0, loc + 3) + return to_return + + +func _handle_tree_item_select(item, force_scroll): + var item_type = item.get_metadata(0).type + + var path = ''; + var line = -1; + var method_name = '' + var inner_class = '' + + if(item_type == 'test'): + var s_item = item.get_parent() + path = s_item.get_metadata(0)['path'] + inner_class = s_item.get_metadata(0)['inner_class'] + line = -1 + method_name = item.get_text(0) + elif(item_type == 'assert'): + var s_item = item.get_parent().get_parent() + path = s_item.get_metadata(0)['path'] + inner_class = s_item.get_metadata(0)['inner_class'] + + line = _get_line_number_from_assert_msg(item.get_text(0)) + method_name = item.get_parent().get_text(0) + elif(item_type == 'script'): + path = item.get_metadata(0)['path'] + if(item.get_parent() != _root): + inner_class = item.get_text(0) + line = -1 + method_name = '' + else: + return + + var path_info = _get_path_and_inner_class_name_from_test_path(path) + if(force_scroll or _ctrls.toolbar.show_script.pressed): + _goto_code(path, line, method_name, inner_class) + if(force_scroll or _ctrls.toolbar.scroll_output.pressed): + _goto_output(path, method_name, inner_class) + + +# starts at beginning of text edit and searches for each search term, moving +# through the text as it goes; ensuring that, when done, it found the first +# occurance of the last srting that happend after the first occurance of +# each string before it. (Generic way of searching for a method name in an +# inner class that may have be a duplicate of a method name in a different +# inner class) +func _get_line_number_for_seq_search(search_strings, te): +# var te = _editors.get_current_text_edit() + var result = null + var to_return = -1 + var start_line = 0 + var start_col = 0 + var s_flags = 0 + + var i = 0 + var string_found = true + while(i < search_strings.size() and string_found): + result = te.search(search_strings[i], s_flags, start_line, start_col) + if(result.size() > 0): + start_line = result[TextEdit.SEARCH_RESULT_LINE] + start_col = result[TextEdit.SEARCH_RESULT_COLUMN] + to_return = start_line + else: + string_found = false + i += 1 + + return to_return + + +func _goto_code(path, line, method_name='', inner_class =''): + if(_interface == null): + print('going to ', [path, line, method_name, inner_class]) + return + + _open_file(path, line) + if(line == -1): + var search_strings = [] + if(inner_class != ''): + search_strings.append(inner_class) + + if(method_name != ''): + search_strings.append(method_name) + + line = _get_line_number_for_seq_search(search_strings, _editors.get_current_text_edit()) + if(line != -1): + _interface.get_script_editor().goto_line(line) + + +func _goto_output(path, method_name, inner_class): + if(_output_control == null): + return + + var search_strings = [path] + + if(inner_class != ''): + search_strings.append(inner_class) + + if(method_name != ''): + search_strings.append(method_name) + + var line = _get_line_number_for_seq_search(search_strings, _output_control.get_rich_text_edit()) + if(line != -1): + _output_control.scroll_to_line(line) + + +func _show_all_passed(): + if(_root.get_children() == null): + add_centered_text('Everything passed!') + + +func _set_collapsed_on_all(item, value): + if(item == _root): + var node = _root.get_children() + while(node != null): + node.call_recursive('set_collapsed', value) + node = node.get_next() + else: + item.call_recursive('set_collapsed', value) + +# -------------- +# Events +# -------------- +func _on_Tree_item_selected(): + # do not force scroll + var item = _ctrls.tree.get_selected() + _handle_tree_item_select(item, false) + # it just looks better if the left is always selected. + if(item.is_selected(1)): + item.deselect(1) + item.select(0) + + +func _on_Tree_item_activated(): + # force scroll + print('double clicked') + _handle_tree_item_select(_ctrls.tree.get_selected(), true) + +func _on_Collapse_pressed(): + collapse_selected() + + +func _on_Expand_pressed(): + expand_selected() + + +func _on_CollapseAll_pressed(): + collapse_all() + + +func _on_ExpandAll_pressed(): + expand_all() + + +func _on_Hide_Passing_pressed(): + _hide_passing = _ctrls.toolbar.hide_passing.pressed + +# -------------- +# Public +# -------------- +func load_json_file(path): + var text = _utils.get_file_as_text(path) + if(text != ''): + var result = JSON.parse(text) + if(result.error != OK): + add_centered_text(str(path, " has invalid json in it \n", + 'Error ', result.error, "@", result.error_line, "\n", + result.error_string)) + return + + load_json_results(result.result) + else: + add_centered_text(str(path, ' was empty or does not exist.')) + + +func load_json_results(j): + clear() + add_centered_text('Nothing Here') + _load_result_tree(j) + + +func add_centered_text(t): + _ctrls.lbl_overlay.text = t + + +func clear_centered_text(): + _ctrls.lbl_overlay.text = '' + + +func clear(): + _ctrls.tree.clear() + _root = _ctrls.tree.create_item() + clear_centered_text() + + +func set_interface(which): + _interface = which + + +func set_script_text_editors(value): + _editors = value + + +func collapse_all(): + _set_collapsed_on_all(_root, true) + + +func expand_all(): + _set_collapsed_on_all(_root, false) + + +func collapse_selected(): + var item = _ctrls.tree.get_selected() + if(item != null): + _set_collapsed_on_all(item, true) + +func expand_selected(): + var item = _ctrls.tree.get_selected() + if(item != null): + _set_collapsed_on_all(item, false) + + +func set_show_orphans(should): + _show_orphans = should + + +func set_font(font_name, size): + pass +# var dyn_font = DynamicFont.new() +# var font_data = DynamicFontData.new() +# font_data.font_path = 'res://addons/gut/fonts/' + font_name + '-Regular.ttf' +# font_data.antialiased = true +# dyn_font.font_data = font_data +# +# _font = dyn_font +# _font.size = size +# _font_size = size + + +func set_output_control(value): + _output_control = value diff --git a/addons/gut/gui/RunResults.tscn b/addons/gut/gui/RunResults.tscn new file mode 100644 index 0000000..be4c1d5 --- /dev/null +++ b/addons/gut/gui/RunResults.tscn @@ -0,0 +1,165 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/gut/gui/RunResults.gd" type="Script" id=1] + +[sub_resource type="Image" id=3] +data = { +"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), +"format": "LumAlpha8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id=2] +flags = 4 +flags = 4 +image = SubResource( 3 ) +size = Vector2( 16, 16 ) + +[node name="RunResults" type="Control"] +margin_right = 595.0 +margin_bottom = 459.0 +rect_min_size = Vector2( 302, 0 ) +script = ExtResource( 1 ) + +[node name="VBox" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="Toolbar" type="HBoxContainer" parent="VBox"] +margin_right = 296.0 +margin_bottom = 24.0 +size_flags_horizontal = 0 + +[node name="Expand" type="ToolButton" parent="VBox/Toolbar"] +margin_right = 28.0 +margin_bottom = 24.0 +hint_tooltip = "Expand selected item and all children." +icon = SubResource( 2 ) + +[node name="Collapse" type="ToolButton" parent="VBox/Toolbar"] +margin_left = 32.0 +margin_right = 60.0 +margin_bottom = 24.0 +hint_tooltip = "Collapse selected item and all children." +icon = SubResource( 2 ) + +[node name="Sep" type="ColorRect" parent="VBox/Toolbar"] +margin_left = 64.0 +margin_right = 66.0 +margin_bottom = 24.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="LblAll" type="Label" parent="VBox/Toolbar"] +margin_left = 70.0 +margin_top = 5.0 +margin_right = 91.0 +margin_bottom = 19.0 +text = "All:" +align = 1 + +[node name="ExpandAll" type="ToolButton" parent="VBox/Toolbar"] +margin_left = 95.0 +margin_right = 123.0 +margin_bottom = 24.0 +hint_tooltip = "Expand All." +icon = SubResource( 2 ) + +[node name="CollapseAll" type="ToolButton" parent="VBox/Toolbar"] +margin_left = 127.0 +margin_right = 155.0 +margin_bottom = 24.0 +hint_tooltip = "Collapse all." +icon = SubResource( 2 ) + +[node name="Sep2" type="ColorRect" parent="VBox/Toolbar"] +margin_left = 159.0 +margin_right = 161.0 +margin_bottom = 24.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="HidePassing" type="CheckBox" parent="VBox/Toolbar"] +margin_left = 165.0 +margin_right = 189.0 +margin_bottom = 24.0 +hint_tooltip = "Show/Hide passing tests. Takes effect on next run." +size_flags_horizontal = 4 +custom_icons/checked = SubResource( 2 ) +custom_icons/unchecked = SubResource( 2 ) +pressed = true +__meta__ = { +"_editor_description_": "" +} + +[node name="Sep3" type="ColorRect" parent="VBox/Toolbar"] +margin_left = 193.0 +margin_right = 195.0 +margin_bottom = 24.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="LblSync" type="Label" parent="VBox/Toolbar"] +margin_left = 199.0 +margin_top = 5.0 +margin_right = 232.0 +margin_bottom = 19.0 +text = "Sync:" +align = 1 + +[node name="ShowScript" type="ToolButton" parent="VBox/Toolbar"] +margin_left = 236.0 +margin_right = 264.0 +margin_bottom = 24.0 +hint_tooltip = "Open script and scroll to line when a tree item is clicked." +toggle_mode = true +pressed = true +icon = SubResource( 2 ) + +[node name="ScrollOutput" type="ToolButton" parent="VBox/Toolbar"] +margin_left = 268.0 +margin_right = 296.0 +margin_bottom = 24.0 +hint_tooltip = "Scroll to related line in the output panel when tree item clicked." +toggle_mode = true +pressed = true +icon = SubResource( 2 ) + +[node name="Output" type="Panel" parent="VBox"] +self_modulate = Color( 1, 1, 1, 0.541176 ) +margin_top = 28.0 +margin_right = 595.0 +margin_bottom = 459.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Scroll" type="ScrollContainer" parent="VBox/Output"] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="Tree" type="Tree" parent="VBox/Output/Scroll"] +margin_right = 595.0 +margin_bottom = 431.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +columns = 2 +hide_root = true + +[node name="OverlayMessage" type="Label" parent="VBox/Output"] +anchor_right = 1.0 +anchor_bottom = 1.0 +align = 1 +valign = 1 + +[node name="FontSampler" type="Label" parent="."] +visible = false +margin_right = 40.0 +margin_bottom = 14.0 +text = "000 of 000 passed" + +[connection signal="pressed" from="VBox/Toolbar/Expand" to="." method="_on_Expand_pressed"] +[connection signal="pressed" from="VBox/Toolbar/Collapse" to="." method="_on_Collapse_pressed"] +[connection signal="pressed" from="VBox/Toolbar/ExpandAll" to="." method="_on_ExpandAll_pressed"] +[connection signal="pressed" from="VBox/Toolbar/CollapseAll" to="." method="_on_CollapseAll_pressed"] +[connection signal="pressed" from="VBox/Toolbar/HidePassing" to="." method="_on_Hide_Passing_pressed"] +[connection signal="item_activated" from="VBox/Output/Scroll/Tree" to="." method="_on_Tree_item_activated"] +[connection signal="item_selected" from="VBox/Output/Scroll/Tree" to="." method="_on_Tree_item_selected"] diff --git a/addons/gut/gui/arrow.png b/addons/gut/gui/arrow.png index 09d8323..26a7dc4 100644 --- a/addons/gut/gui/arrow.png +++ b/addons/gut/gui/arrow.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9918e125593427685252d339f9c49d2ce8ce0b0fff9d28089164d78fccd80e2 -size 645 +oid sha256:7f5a2f25d0cc68b8281bda24cdcd1d816e718d11fa0d050ede6cbad9033d17ed +size 122 diff --git a/addons/gut/gui/gut_config_gui.gd b/addons/gut/gui/gut_config_gui.gd index dc58be4..4f056ff 100644 --- a/addons/gut/gui/gut_config_gui.gd +++ b/addons/gut/gui/gut_config_gui.gd @@ -272,8 +272,8 @@ func get_config_issues(): if(!has_directory): to_return.append('You do not have any directories set.') - if(_cfg_ctrls['prefix'].text == ''): - to_return.append("You must set a Script prefix or GUT won't find any scripts") + if(!_cfg_ctrls['suffix'].text.ends_with('.gd')): + to_return.append("Script suffix must end in '.gd'") return to_return @@ -354,6 +354,8 @@ func set_options(options): _add_title('Misc') _add_value('prefix', options.prefix, 'Script Prefix', "The filename prefix for all test scripts.") + _add_value('suffix', options.suffix, 'Script Suffix', + "Script suffix, including .gd extension. For example '_foo.gd'.") func get_options(base_opts): @@ -404,5 +406,6 @@ func get_options(base_opts): # Misc to_return.prefix = _cfg_ctrls.prefix.text + to_return.suffix = _cfg_ctrls.suffix.text return to_return diff --git a/addons/gut/gui/play.png b/addons/gut/gui/play.png index 697f319..2be1d4d 100644 --- a/addons/gut/gui/play.png +++ b/addons/gut/gui/play.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90041b369445e15dfaa4ec011cb4f1c0536a8d63295a574e0f8743f772dc418e -size 676 +oid sha256:d4280462a7940ebaf184e2b92bd01a69cf09ae1c97b175ac92e1410d16ecf278 +size 143 diff --git a/addons/gut/gut.gd b/addons/gut/gut.gd index bc00f76..6e5dc02 100644 --- a/addons/gut/gut.gd +++ b/addons/gut/gut.gd @@ -38,9 +38,6 @@ var _inner_class_name = '' var _should_maximize = false setget set_should_maximize, get_should_maximize var _log_level = 1 setget set_log_level, get_log_level var _disable_strict_datatype_checks = false setget disable_strict_datatype_checks, is_strict_datatype_checks_disabled -var _test_prefix = 'test_' -var _file_prefix = 'test_' -var _file_extension = '.gd' var _inner_class_prefix = 'Test' var _temp_directory = 'user://gut_temp_directory' var _export_path = '' setget set_export_path, get_export_path @@ -138,6 +135,8 @@ var _before_all_test_obj = load('res://addons/gut/test_collector.gd').Test.new() # Used for proper assert tracking and printing during after_all var _after_all_test_obj = load('res://addons/gut/test_collector.gd').Test.new() + +var _file_prefix = 'test_' const SIGNAL_TESTS_FINISHED = 'tests_finished' const SIGNAL_STOP_YIELD_BEFORE_TEARDOWN = 'stop_yield_before_teardown' @@ -602,33 +601,36 @@ func _do_yield_between(frames=2): _yield_frames = frames return self + # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func _wait_for_done(result): - var iter_counter = 0 - var print_after = 3 - # callback method sets waiting to false. result.connect(COMPLETED, self, '_on_test_script_yield_completed') if(!_was_yield_method_called): - _lgr.log('-- Yield detected, waiting --', _lgr.fmts.yellow) + _lgr.yield_msg('-- Yield detected, waiting --') _was_yield_method_called = false _waiting = true - _wait_timer.set_wait_time(0.4) + var cycles_per_dot = 500 + var cycles = 0 var dots = '' + while(_waiting): - iter_counter += 1 - _lgr.yield_text('waiting' + dots) - _wait_timer.start() - yield(_wait_timer, 'timeout') - dots += '.' - if(dots.length() > 5): - dots = '' + yield(get_tree(), 'idle_frame') + cycles += 1 + + if(cycles >= cycles_per_dot): + cycles = 0 + dots += '.' + if(dots.length() > 5): + dots = '' + _lgr.yield_text('waiting' + dots) _lgr.end_yield() + # ------------------------------------------------------------------------------ # returns self so it can be integrated into the yield call. # ------------------------------------------------------------------------------ @@ -637,6 +639,7 @@ func _wait_for_continue_button(): _waiting = true return self + # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func _call_deprecated_script_method(script, method, alt): @@ -895,8 +898,11 @@ func _test_the_scripts(indexes=[]): # _run_test calls _wait for done, just wait for that to finish yield(script_result, COMPLETED) - if(_current_test.assert_count == 0 and !_current_test.pending): + if(!_current_test.did_assert()): _lgr.warn('Test did not assert') + + _gui.add_test(_current_test.did_pass()) + _current_test.has_printed_name = false _gui.set_progress_test_value(i + 1) emit_signal('test_finished') @@ -949,7 +955,8 @@ func _pass(text=''): func _fail(text=''): _gui.add_failing() # increments counters if(_current_test != null): - var line_text = ' at line ' + str(_extract_line_number(_current_test)) + var line_number = _extract_line_number(_current_test) + var line_text = ' at line ' + str(line_number) p(line_text, LOG_LEVEL_FAIL_ONLY) # format for summary line_text = "\n " + line_text @@ -959,6 +966,7 @@ func _fail(text=''): _new_summary.add_fail(_current_test.name, call_count_text + text + line_text) _current_test.passed = false _current_test.assert_count += 1 + _current_test.line_number = line_number else: if(_new_summary != null): # b/c of tests. _new_summary.add_fail('script level', text) @@ -1074,7 +1082,9 @@ func test_scripts(run_rest=false): if(_script_name != null and _script_name != ''): var indexes = _get_indexes_matching_script_name(_script_name) if(indexes == []): - _lgr.error('Could not find script matching ' + _script_name) + _lgr.error(str( + "Could not find script matching '", _script_name, "'.\n", + "Check your directory settings and Script Prefix/Suffix settings.")) else: _test_the_scripts(indexes) else: @@ -1110,7 +1120,7 @@ func add_script(script): # with the suffix. Does not look in sub directories. Can be called multiple # times. # ------------------------------------------------------------------------------ -func add_directory(path, prefix=_file_prefix, suffix=_file_extension): +func add_directory(path, prefix=_file_prefix, suffix=".gd"): # check for '' b/c the calls to addin the exported directories 1-6 will pass # '' if the field has not been populated. This will cause res:// to be # processed which will include all files if include_subdirectories is true. @@ -1124,7 +1134,9 @@ func add_directory(path, prefix=_file_prefix, suffix=_file_extension): else: var files = _get_files(path, prefix, suffix) for i in range(files.size()): - add_script(files[i]) + if(_script_name == null or _script_name == '' or \ + (_script_name != null and files[i].findn(_script_name) != -1)): + add_script(files[i]) # ------------------------------------------------------------------------------ @@ -1358,7 +1370,7 @@ func set_yield_time(time, text=''): msg += ' --' else: msg += ': ' + text + ' --' - _lgr.log(msg, _lgr.fmts.yellow) + _lgr.yield_msg(msg) _was_yield_method_called = true return self @@ -1375,7 +1387,7 @@ func set_yield_frames(frames, text=''): msg += ' --' else: msg += ': ' + text + ' --' - _lgr.log(msg, _lgr.fmts.yellow) + _lgr.yield_msg(msg) _was_yield_method_called = true _yield_frames = max(frames + 1, 1) @@ -1393,7 +1405,7 @@ func set_yield_signal_or_time(obj, signal_name, max_wait, text=''): _yield_timer.set_wait_time(max_wait) _yield_timer.start() _was_yield_method_called = true - _lgr.log(str('-- Yielding to signal "', signal_name, '" or for ', max_wait, ' seconds -- ', text), _lgr.fmts.yellow) + _lgr.yield_msg(str('-- Yielding to signal "', signal_name, '" or for ', max_wait, ' seconds -- ', text)) return self # ------------------------------------------------------------------------------ diff --git a/addons/gut/gut_cmdln.gd b/addons/gut/gut_cmdln.gd index e1a0c0a..37221c5 100644 --- a/addons/gut/gut_cmdln.gd +++ b/addons/gut/gut_cmdln.gd @@ -133,7 +133,7 @@ func setup_options(options, font_names): opts.add('-gtest', [], 'Comma delimited list of full paths to test scripts to run.') opts.add('-gdir', options.dirs, 'Comma delimited list of directories to add tests from.') opts.add('-gprefix', options.prefix, 'Prefix used to find tests when specifying -gdir. Default "[default]".') - opts.add('-gsuffix', options.suffix, 'Suffix used to find tests when specifying -gdir. Default "[default]".') + opts.add('-gsuffix', options.suffix, 'Test script suffix, including .gd extension. Default "[default]".') opts.add('-ghide_orphans', false, 'Display orphan counts for tests and scripts. Default "[default]".') opts.add('-gmaximize', false, 'Maximizes test runner window to fit the viewport.') opts.add('-gcompact_mode', false, 'The runner will be in compact mode. This overrides -gmaximize.') diff --git a/addons/gut/gut_config.gd b/addons/gut/gut_config.gd index 7a582d3..26a568f 100644 --- a/addons/gut/gut_config.gd +++ b/addons/gut/gut_config.gd @@ -45,7 +45,11 @@ var default_options = { var default_panel_options = { font_name = 'CourierPrime', - font_size = 30 + font_size = 30, + hide_result_tree = false, + hide_output_text = false, + hide_settings = false, + use_colors = true } var options = default_options.duplicate() @@ -86,13 +90,21 @@ func _load_options_from_config_file(file_path, into): # Get all the options out of the config file using the option name. The # options hash is now the default source of truth for the name of an option. - for key in into: - if(results.result.has(key)): - if(results.result[key] != null): - into[key] = results.result[key] + _load_dict_into(results.result, into) return 1 +func _load_dict_into(source, dest): + for key in dest: + if(source.has(key)): + if(source[key] != null): + if(typeof(source[key]) == TYPE_DICTIONARY): + _load_dict_into(source[key], dest[key]) + else: + dest[key] = source[key] + + + func write_options(path): var content = JSON.print(options, ' ') @@ -125,15 +137,16 @@ func _apply_options(opts, _tester): _tester.set_log_level(opts.log_level) _tester.set_ignore_pause_before_teardown(opts.ignore_pause) + if(opts.selected != ''): + _tester.select_script(opts.selected) + # _run_single = true + for i in range(opts.dirs.size()): _tester.add_directory(opts.dirs[i], opts.prefix, opts.suffix) for i in range(opts.tests.size()): _tester.add_script(opts.tests[i]) - if(opts.selected != ''): - _tester.select_script(opts.selected) - # _run_single = true if(opts.double_strategy == 'full'): _tester.set_double_strategy(DOUBLE_STRATEGY.FULL) diff --git a/addons/gut/gut_plugin.gd b/addons/gut/gut_plugin.gd index fec9742..9e8743a 100644 --- a/addons/gut/gut_plugin.gd +++ b/addons/gut/gut_plugin.gd @@ -3,6 +3,7 @@ extends EditorPlugin var _bottom_panel = null + func _enter_tree(): _bottom_panel = preload('res://addons/gut/gui/GutBottomPanel.tscn').instance() # Initialization of the plugin goes here @@ -24,4 +25,4 @@ func _exit_tree(): # Always remember to remove it from the engine when deactivated remove_custom_type("Gut") remove_control_from_bottom_panel(_bottom_panel) - _bottom_panel.free()
\ No newline at end of file + _bottom_panel.free() diff --git a/addons/gut/icon.png b/addons/gut/icon.png index e2febb6..430b7c8 100644 --- a/addons/gut/icon.png +++ b/addons/gut/icon.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:012b8bf72b890c5133633511fa0c5a45da3d501ee8dd267924292f267facb044 -size 320 +oid sha256:17186e5c117662f093554ade93529a8f2927750362004e133acf3ded0afba45d +size 129 diff --git a/addons/gut/images/green.png b/addons/gut/images/green.png new file mode 100644 index 0000000..1652636 --- /dev/null +++ b/addons/gut/images/green.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9137132eda912531252c215d698a604c401f2e59f8b29005117d72fef625ed75 +size 213 diff --git a/addons/gut/images/green.png.import b/addons/gut/images/green.png.import new file mode 100644 index 0000000..2537314 --- /dev/null +++ b/addons/gut/images/green.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/green.png-e3a17091688e10a7013279b38edc7f8a.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/gut/images/green.png" +dest_files=[ "res://.import/green.png-e3a17091688e10a7013279b38edc7f8a.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/addons/gut/images/red.png b/addons/gut/images/red.png new file mode 100644 index 0000000..f2770fd --- /dev/null +++ b/addons/gut/images/red.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e85d15987192029471051afc3b5de797629b95a5e02024fc5d14d81fdaf599bd +size 213 diff --git a/addons/gut/images/red.png.import b/addons/gut/images/red.png.import new file mode 100644 index 0000000..2932b46 --- /dev/null +++ b/addons/gut/images/red.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/red.png-47a557c3922e800f76686bc1a4ad0c3c.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/gut/images/red.png" +dest_files=[ "res://.import/red.png-47a557c3922e800f76686bc1a4ad0c3c.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/addons/gut/images/yellow.png b/addons/gut/images/yellow.png new file mode 100644 index 0000000..6ca993b --- /dev/null +++ b/addons/gut/images/yellow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3e42a49cde1baf9dd2647d62ca58399efeb94c2eec39b2b62450021532c08c3 +size 213 diff --git a/addons/gut/images/yellow.png.import b/addons/gut/images/yellow.png.import new file mode 100644 index 0000000..ae59337 --- /dev/null +++ b/addons/gut/images/yellow.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/yellow.png-b3cf3d463958a169d909273d3d742052.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/gut/images/yellow.png" +dest_files=[ "res://.import/yellow.png-b3cf3d463958a169d909273d3d742052.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/addons/gut/input_factory.gd b/addons/gut/input_factory.gd index 5eb3a67..5505553 100644 --- a/addons/gut/input_factory.gd +++ b/addons/gut/input_factory.gd @@ -55,14 +55,14 @@ static func _to_scancode(which): static func new_mouse_button_event(position, global_position, pressed, button_index): - var event = InputEventMouseButton.new() - event.position = position - if(global_position != null): - event.global_position = global_position - event.pressed = pressed - event.button_index = button_index - - return event + var event = InputEventMouseButton.new() + event.position = position + if(global_position != null): + event.global_position = global_position + event.pressed = pressed + event.button_index = button_index + + return event static func key_up(which): diff --git a/addons/gut/input_sender.gd b/addons/gut/input_sender.gd index e9344d7..155ea64 100644 --- a/addons/gut/input_sender.gd +++ b/addons/gut/input_sender.gd @@ -92,6 +92,7 @@ class InputQueueItem: var t = _delay_timer(time_delay) t.connect("timeout", self, "_on_time_timeout") + # ############################################################################## # # ############################################################################## @@ -116,6 +117,8 @@ var _pressed_keys = {} var _pressed_actions = {} var _pressed_mouse_buttons = {} +var _auto_flush_input = false + signal idle @@ -141,6 +144,8 @@ func _send_event(event): for r in _receivers: if(r == Input): Input.parse_input_event(event) + if(_auto_flush_input): + Input.flush_buffered_events() else: if(r.has_method("_input")): r._input(event) @@ -363,3 +368,11 @@ func is_action_pressed(which): func is_mouse_button_pressed(which): return _pressed_mouse_buttons.has(which) and _pressed_mouse_buttons[which] + + +func get_auto_flush_input(): + return _auto_flush_input + + +func set_auto_flush_input(val): + _auto_flush_input = val diff --git a/addons/gut/logger.gd b/addons/gut/logger.gd index 900b928..5932343 100644 --- a/addons/gut/logger.gd +++ b/addons/gut/logger.gd @@ -333,12 +333,19 @@ func _end_yield_gui(): lbl.visible = false lbl.set_text('') +# This is used for displaying the "yield detected" and "yielding to" messages. +func yield_msg(text): + if(_type_data.warn.enabled): + self.log(text, fmts.yellow) + +# This is used for the animated "waiting" message func yield_text(text): _yield_text_terminal(text) _yield_text_gui(text) _last_yield_text = text _yield_calls += 1 +# This is used for the animated "waiting" message func end_yield(): if(_yield_calls == 0): return @@ -346,3 +353,6 @@ func end_yield(): _end_yield_gui() _yield_calls = 0 _last_yield_text = '' + +func get_gui_bbcode(): + return _printers.gui.get_bbcode() diff --git a/addons/gut/method_maker.gd b/addons/gut/method_maker.gd index a807e26..11586b9 100644 --- a/addons/gut/method_maker.gd +++ b/addons/gut/method_maker.gd @@ -58,6 +58,7 @@ const PARAM_PREFIX = 'p_' # TYPE_RID = 16 — Variable is of type RID. # TYPE_INT_ARRAY = 21 — Variable is of type PoolIntArray. # TYPE_REAL_ARRAY = 22 — Variable is of type PoolRealArray. + # TYPE_STRING_ARRAY = 23 — Variable is of type PoolStringArray. # TYPE_PLANE = 9 — Variable is of type Plane. @@ -66,7 +67,6 @@ const PARAM_PREFIX = 'p_' # TYPE_BASIS = 12 — Variable is of type Basis. # TYPE_NODE_PATH = 15 — Variable is of type NodePath. # TYPE_RAW_ARRAY = 20 — Variable is of type PoolByteArray. -# TYPE_STRING_ARRAY = 23 — Variable is of type PoolStringArray. # TYPE_VECTOR3_ARRAY = 25 — Variable is of type PoolVector3Array. # TYPE_COLOR_ARRAY = 26 — Variable is of type PoolColorArray. # TYPE_MAX = 27 — Marker for end of type constants. @@ -98,14 +98,16 @@ func _init(): _supported_defaults[TYPE_TRANSFORM] = 'Transform' _supported_defaults[TYPE_INT_ARRAY] = 'PoolIntArray' _supported_defaults[TYPE_REAL_ARRAY] = 'PoolRealArray' + _supported_defaults[TYPE_STRING_ARRAY] = 'PoolStringArray' # ############### # Private # ############### var _func_text = _utils.get_file_as_text('res://addons/gut/double_templates/function_template.txt') +var _init_text = _utils.get_file_as_text('res://addons/gut/double_templates/init_template.txt') func _is_supported_default(type_flag): - return type_flag >= 0 and type_flag < _supported_defaults.size() and [type_flag] != null + return type_flag >= 0 and type_flag < _supported_defaults.size() and _supported_defaults[type_flag] != null func _make_stub_default(method, index): @@ -139,7 +141,7 @@ func _make_arg_array(method_meta, override_size): else: dflt_text = str(_supported_defaults[t], str(method_meta.default_args[dflt_idx]).to_lower()) elif(t == TYPE_TRANSFORM): - #value will be 4 Vector3 and look like: 1, 0, 0, 0, 1, 0, 0, 0, 1 - 0, 0, 0 + # value will be 4 Vector3 and look like: 1, 0, 0, 0, 1, 0, 0, 0, 1 - 0, 0, 0 var sections = str(method_meta.default_args[dflt_idx]).split("-") var vecs = sections[0].split(",") vecs.append_array(sections[1].split(",")) @@ -157,10 +159,9 @@ func _make_arg_array(method_meta, override_size): dflt_text = str(_supported_defaults[t], "(", vectors, ")") elif(t == TYPE_RID): dflt_text = str(_supported_defaults[t], 'null') - elif(t in [TYPE_REAL_ARRAY, TYPE_INT_ARRAY]): + elif(t in [TYPE_REAL_ARRAY, TYPE_INT_ARRAY, TYPE_STRING_ARRAY]): dflt_text = str(_supported_defaults[t], "()") - - # Everything else puts the prefix (if one is there) form _supported_defaults + # Everything else puts the prefix (if one is there) from _supported_defaults # in front. The to_lower is used b/c for some reason the defaults for # null, true, false are all "Null", "True", "False". else: @@ -230,10 +231,35 @@ func _get_spy_call_parameters_text(args): # Public # ############### +func _get_init_text(meta, args, method_params, param_array): + var text = null + + var decleration = str('func ', meta.name, '(', method_params, ')') + var super_params = '' + if(args.size() > 0): + super_params = '.(' + for i in range(args.size()): + super_params += args[i].p_name + if(i != args.size() -1): + super_params += ', ' + super_params += ')' + + text = _init_text.format({ + "func_decleration":decleration, + "super_params":super_params, + "param_array":param_array, + "method_name":meta.name + }) + + return text + + # Creates a delceration for a function based off of function metadata. All # types whose defaults are supported will have their values. If a datatype # is not supported and the parameter has a default, a warning message will be # printed and the declaration will return null. +# +# path is no longer used func get_function_text(meta, path=null, override_size=null, super_name=""): var method_params = '' var text = null @@ -255,13 +281,16 @@ func get_function_text(meta, path=null, override_size=null, super_name=""): param_array = '[]' if(method_params != null): - var decleration = str('func ', meta.name, '(', method_params, '):') - text = _func_text.format({ - "func_decleration":decleration, - "method_name":meta.name, - "param_array":param_array, - "super_call":_get_super_call_text(meta.name, args, super_name) - }) + if(meta.name == '_init'): + text = _get_init_text(meta, args, method_params, param_array) + else: + var decleration = str('func ', meta.name, '(', method_params, '):') + text = _func_text.format({ + "func_decleration":decleration, + "method_name":meta.name, + "param_array":param_array, + "super_call":_get_super_call_text(meta.name, args, super_name) + }) return text diff --git a/addons/gut/orphan_counter.gd b/addons/gut/orphan_counter.gd index cbfbea4..b31e2b6 100644 --- a/addons/gut/orphan_counter.gd +++ b/addons/gut/orphan_counter.gd @@ -52,4 +52,4 @@ func print_orphans(name, lgr): var o = 'orphan' if(count > 1): o = 'orphans' - lgr.orphan(str(count, ' new ', o, '(', name, ').')) + lgr.orphan(str(count, ' new ', o, ' in ', name, '.')) diff --git a/addons/gut/parameter_factory.gd b/addons/gut/parameter_factory.gd index fe99e89..6bffe4c 100644 --- a/addons/gut/parameter_factory.gd +++ b/addons/gut/parameter_factory.gd @@ -72,4 +72,4 @@ static func named_parameters(names, values): # * File. IDK what it would look like. csv maybe. # * Random values within a range? # * All int values in a range or add an optioanal step. -# *
\ No newline at end of file +# * diff --git a/addons/gut/parameter_handler.gd b/addons/gut/parameter_handler.gd index 507ef71..6b37e82 100644 --- a/addons/gut/parameter_handler.gd +++ b/addons/gut/parameter_handler.gd @@ -34,4 +34,4 @@ func get_call_count(): return _call_count func get_parameter_count(): - return _params.size()
\ No newline at end of file + return _params.size() diff --git a/addons/gut/plugin.cfg b/addons/gut/plugin.cfg index d052cb0..ee669be 100644 --- a/addons/gut/plugin.cfg +++ b/addons/gut/plugin.cfg @@ -3,5 +3,5 @@ name="Gut" description="Unit Testing tool for Godot." author="Butch Wesley" -version="7.3.0" +version="7.4.1" script="gut_plugin.gd" diff --git a/addons/gut/plugin_control.gd b/addons/gut/plugin_control.gd index c363b2a..0e14cea 100644 --- a/addons/gut/plugin_control.gd +++ b/addons/gut/plugin_control.gd @@ -69,13 +69,10 @@ export var _yield_between_tests = true # development to prevent any breaking changes and will likely be removed in # the future. export var _disable_strict_datatype_checks = false -# The prefix used to find test methods. -export var _test_prefix = 'test_' # The prefix used to find test scripts. export var _file_prefix = 'test_' -# The file extension for test scripts (I don't think you can change this and -# everythign work). -export var _file_extension = '.gd' +# The suffix used to find test scripts. +export var _file_suffix = '.gd' # The prefix used to find Inner Test Classes. export var _inner_class_prefix = 'Test' # The directory GUT will use to write any temporary files. This isn't used @@ -181,9 +178,7 @@ func _setup_gut(): _gut._tests_like = _tests_like _gut._inner_class_name = _inner_class_name - _gut._test_prefix = _test_prefix _gut._file_prefix = _file_prefix - _gut._file_extension = _file_extension _gut._inner_class_prefix = _inner_class_prefix _gut._temp_directory = _temp_directory diff --git a/addons/gut/printers.gd b/addons/gut/printers.gd index c543603..7636eb6 100644 --- a/addons/gut/printers.gd +++ b/addons/gut/printers.gd @@ -63,6 +63,22 @@ class GutGuiPrinter: func _color_text(text, c_word): return '[color=' + c_word + ']' + text + '[/color]' + # Remember, we have to use push and pop because the output from the tests + # can contain [] in it which can mess up the formatting. There is no way + # as of 3.4 that you can get the bbcode out of RTL when using push and pop. + # + # The only way we could get around this is by adding in non-printable + # whitespace after each "[" that is in the text. Then we could maybe do + # this another way and still be able to get the bbcode out, or generate it + # at the same time in a buffer (like we tried that one time). + # + # Since RTL doesn't have good search and selection methods, and those are + # really handy in the editor, it isn't worth making bbcode that can be used + # there as well. + # + # You'll try to get it so the colors can be the same in the editor as they + # are in the output. Good luck, and I hope I typed enough to not go too + # far that rabbit hole before finding out it's not worth it. func format_text(text, fmt): var box = _gut.get_gui().get_text_box() @@ -96,6 +112,9 @@ class GutGuiPrinter: box.remove_line(box.get_line_count() - 1) box.update() + func get_bbcode(): + return _gut.get_gui().get_text_box().text + # ------------------------------------------------------------------------------ # This AND TerminalPrinter should not be enabled at the same time since it will # result in duplicate output. printraw does not print to the console so i had diff --git a/addons/gut/result_exporter.gd b/addons/gut/result_exporter.gd index 3a529ed..753a0c9 100644 --- a/addons/gut/result_exporter.gd +++ b/addons/gut/result_exporter.gd @@ -15,7 +15,7 @@ func _export_tests(summary_script): "passing":tests[key].pass_texts, "failing":tests[key].fail_texts, "pending":tests[key].pending_texts, - "orphans":tests[key].orphans, + "orphans":tests[key].orphans } return to_return diff --git a/addons/gut/stub_params.gd b/addons/gut/stub_params.gd index d52d9b2..1048488 100644 --- a/addons/gut/stub_params.gd +++ b/addons/gut/stub_params.gd @@ -1,3 +1,6 @@ +var _utils = load('res://addons/gut/utils.gd').get_instance() +var _lgr = _utils.get_logger() + var return_val = null var stub_target = null var target_subpath = null @@ -29,20 +32,31 @@ func _init(target=null, method=null, subpath=null): stub_method = method target_subpath = subpath + func to_return(val): - return_val = val - call_super = false - _parameter_override_only = false + if(stub_method == '_init'): + _lgr.error("You cannot stub _init to do nothing. Super's _init is always called.") + else: + return_val = val + call_super = false + _parameter_override_only = false return self + func to_do_nothing(): - return to_return(null) + to_return(null) + return self + func to_call_super(): - call_super = true - _parameter_override_only = false + if(stub_method == '_init'): + _lgr.error("You cannot stub _init to call super. Super's _init is always called.") + else: + call_super = true + _parameter_override_only = false return self + func when_passed(p1=NOT_SET,p2=NOT_SET,p3=NOT_SET,p4=NOT_SET,p5=NOT_SET,p6=NOT_SET,p7=NOT_SET,p8=NOT_SET,p9=NOT_SET,p10=NOT_SET): parameters = [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10] var idx = 0 @@ -53,24 +67,29 @@ func when_passed(p1=NOT_SET,p2=NOT_SET,p3=NOT_SET,p4=NOT_SET,p5=NOT_SET,p6=NOT_S idx += 1 return self + func param_count(x): parameter_count = x return self + func param_defaults(values): parameter_count = values.size() parameter_defaults = values return self + func has_param_override(): return parameter_count != -1 + func is_param_override_only(): var to_return = false if(has_param_override()): to_return = _parameter_override_only return to_return + func to_s(): var base_string = str(stub_target) if(target_subpath != null): diff --git a/addons/gut/stubber.gd b/addons/gut/stubber.gd index a8fc2ac..66c7df8 100644 --- a/addons/gut/stubber.gd +++ b/addons/gut/stubber.gd @@ -106,18 +106,16 @@ func _find_stub(obj, method, parameters=null, find_overloads=false): # a paramerter override stub. elif(null_match != null and !null_match.is_param_override_only()): to_return = null_match - else: - _lgr.warn(str('Call to [', method, '] was not stubbed for the supplied parameters ', parameters, '. Null was returned.')) + + return to_return func add_stub(stub_params): - if(stub_params.stub_method == '_init'): - _lgr.error("You cannot stub _init. Super's _init is ALWAYS called.") - else: - var key = _add_obj_method(stub_params.stub_target, stub_params.stub_method, stub_params.target_subpath) - returns[key][stub_params.stub_method].append(stub_params) + stub_params._lgr = _lgr + var key = _add_obj_method(stub_params.stub_target, stub_params.stub_method, stub_params.target_subpath) + returns[key][stub_params.stub_method].append(stub_params) # Gets a stubbed return value for the object and method passed in. If the @@ -141,6 +139,7 @@ func get_return(obj, method, parameters=null): if(stub_info != null): return stub_info.return_val else: + _lgr.warn(str('Call to [', method, '] was not stubbed for the supplied parameters ', parameters, '. Null was returned.')) return null @@ -180,6 +179,7 @@ func get_parameter_count(obj, method): func get_default_value(obj, method, p_index): var to_return = null var stub_info = _find_stub(obj, method, null, true) + if(stub_info != null and stub_info.parameter_defaults != null and stub_info.parameter_defaults.size() > p_index): diff --git a/addons/gut/summary.gd b/addons/gut/summary.gd index a5ada0e..10cacd8 100644 --- a/addons/gut/summary.gd +++ b/addons/gut/summary.gd @@ -1,12 +1,33 @@ # ------------------------------------------------------------------------------ # Contains all the results of a single test. Allows for multiple asserts results # and pending calls. +# +# When determining the status of a test, check for failing then passing then +# pending. # ------------------------------------------------------------------------------ class Test: var pass_texts = [] var fail_texts = [] var pending_texts = [] var orphans = 0 + var line_number = 0 + + # must have passed an assert and not have any other status to be passing + func is_passing(): + return pass_texts.size() > 0 and fail_texts.size() == 0 and pending_texts.size() == 0 + + # failing takes precedence over everything else, so any failures makes the + # test a failure. + func is_failing(): + return fail_texts.size() > 0 + + # test is only pending if pending was called and the test is not failing. + func is_pending(): + return pending_texts.size() > 0 and fail_texts.size() == 0 + + func did_something(): + return is_passing() or is_failing() or is_pending() + # NOTE: The "failed" and "pending" text must match what is outputted by # the logger in order for text highlighting to occur in summary. @@ -63,15 +84,21 @@ class TestScript: func get_passing_test_count(): var count = 0 for key in _tests: - if(_tests[key].fail_texts.size() == 0 and - _tests[key].pending_texts.size() == 0): + if(_tests[key].is_passing()): count += 1 return count func get_failing_test_count(): var count = 0 for key in _tests: - if(_tests[key].fail_texts.size() != 0): + if(_tests[key].is_failing()): + count += 1 + return count + + func get_risky_count(): + var count = 0 + for key in _tests: + if(!_tests[key].did_something()): count += 1 return count @@ -147,6 +174,7 @@ func get_totals(): passing = 0, pending = 0, failing = 0, + risky = 0, tests = 0, scripts = 0, passing_tests = 0, @@ -154,12 +182,16 @@ func get_totals(): } for i in range(_scripts.size()): + # assert totals totals.passing += _scripts[i].get_pass_count() totals.pending += _scripts[i].get_pending_count() totals.failing += _scripts[i].get_fail_count() + + # test totals totals.tests += _scripts[i]._test_order.size() totals.passing_tests += _scripts[i].get_passing_test_count() totals.failing_tests += _scripts[i].get_failing_test_count() + totals.risky += _scripts[i].get_risky_count() totals.scripts = get_non_inner_class_script_count() @@ -178,7 +210,7 @@ func log_summary_text(lgr): for t in range(_scripts[s]._test_order.size()): var tname = _scripts[s]._test_order[t] var test = _scripts[s].get_test_obj(tname) - if(test.fail_texts.size() > 0 or test.pending_texts.size() > 0): + if(!test.is_passing()): found_failing_or_pending = true lgr.log(str('- ', tname)) lgr.inc_indent() @@ -187,20 +219,29 @@ func log_summary_text(lgr): lgr.failed(test.fail_texts[i]) for i in range(test.pending_texts.size()): lgr.pending(test.pending_texts[i]) + if(!test.did_something()): + lgr.log('[Did not assert]', lgr.fmts.yellow) lgr.dec_indent() lgr.set_indent_level(0) if(!found_failing_or_pending): lgr.log('All tests passed', lgr.fmts.green) + # just picked a non-printable char, dunno if it is a good or bad choice. + var npws = PoolByteArray([31]).get_string_from_ascii() + lgr.log() var _totals = get_totals() lgr.log("Totals", lgr.fmts.yellow) lgr.log(str('Scripts: ', get_non_inner_class_script_count())) lgr.log(str('Passing tests ', _totals.passing_tests)) lgr.log(str('Failing tests ', _totals.failing_tests)) - lgr.log(str('Pending: ', _totals.pending)) - lgr.log(str('Asserts: ', _totals.passing, '/', _totals.failing)) + lgr.log(str('Risky tests ', _totals.risky)) + var pnd=str('Pending: ', _totals.pending) + # add a non printable character so this "pending" isn't highlighted in the + # editor's output panel. + lgr.log(str(npws, pnd)) + lgr.log(str('Asserts: ', _totals.passing, ' of ', _totals.passing + _totals.failing, ' passed')) lgr.set_indent_level(orig_indent) diff --git a/addons/gut/test.gd b/addons/gut/test.gd index 2a80a8d..3fc0e8f 100644 --- a/addons/gut/test.gd +++ b/addons/gut/test.gd @@ -793,13 +793,15 @@ func get_call_count(object, method_name, parameters=null): return gut.get_spy().call_count(object, method_name, parameters) # ------------------------------------------------------------------------------ -# Assert that object is an instance of a_class +# Deprecated. Use assert_is. # ------------------------------------------------------------------------------ func assert_extends(object, a_class, text=''): _lgr.deprecated('assert_extends', 'assert_is') assert_is(object, a_class, text) -# Alias for assert_extends +# ------------------------------------------------------------------------------ +# Assert that object is an instance of a_class +# ------------------------------------------------------------------------------ func assert_is(object, a_class, text=''): var disp = ''#var disp = str('Expected [', _str(object), '] to be type of [', a_class, ']: ', text) var NATIVE_CLASS = 'GDScriptNativeClass' @@ -1511,7 +1513,7 @@ func use_parameters(params): ph = _utils.ParameterHandler.new(params) gut.set_parameter_handler(ph) - var output = str('(call #', ph.get_call_count() + 1, ') with paramters: ', ph.get_current_parameters()) + var output = str('(call #', ph.get_call_count() + 1, ') with parameters: ', ph.get_current_parameters()) _lgr.log(output) _lgr.inc_indent() return ph.next_parameters() @@ -1649,3 +1651,37 @@ func assert_ne_shallow(v1, v2): _pass(result.get_short_summary()) else: _fail(result.get_short_summary()) + + +# ------------------------------------------------------------------------------ +# Checks the passed in version string (x.x.x) against the engine version to see +# if the engine version is less than the expected version. If it is then the +# test is mareked as passed (for a lack of anything better to do). The result +# of the check is returned. +# +# Example: +# if(skip_if_godot_version_lt('3.5.0')): +# return +# ------------------------------------------------------------------------------ +func skip_if_godot_version_lt(expected): + var should_skip = !_utils.is_godot_version_gte(expected) + if(should_skip): + _pass(str('Skipping ', _utils.godot_version(), ' is less than ', expected)) + return should_skip + + +# ------------------------------------------------------------------------------ +# Checks if the passed in version matches the engine version. The passed in +# version can contain just the major, major.minor or major.minor.path. If +# the version is not the same then the test is marked as passed. The result of +# the check is returned. +# +# Example: +# if(skip_if_godot_version_ne('3.4')): +# return +# ------------------------------------------------------------------------------ +func skip_if_godot_version_ne(expected): + var should_skip = !_utils.is_godot_version(expected) + if(should_skip): + _pass(str('Skipping ', _utils.godot_version(), ' is not ', expected)) + return should_skip
\ No newline at end of file diff --git a/addons/gut/test_collector.gd b/addons/gut/test_collector.gd index c8f5d8b..9fe3199 100644 --- a/addons/gut/test_collector.gd +++ b/addons/gut/test_collector.gd @@ -16,6 +16,14 @@ class Test: # if the test has been marked pending at anypont during # execution. var pending = false + # the line number when the test fails + var line_number = -1 + + func did_pass(): + return passed and !pending and assert_count > 0 + + func did_assert(): + return assert_count > 0 or pending # ------------------------------------------------------------------------------ diff --git a/addons/gut/utils.gd b/addons/gut/utils.gd index 50e8950..18032a0 100644 --- a/addons/gut/utils.gd +++ b/addons/gut/utils.gd @@ -98,7 +98,7 @@ var TestCollector = load('res://addons/gut/test_collector.gd') var ThingCounter = load('res://addons/gut/thing_counter.gd') # Source of truth for the GUT version -var version = '7.3.0' +var version = '7.4.1' # The required Godot version as an array. var req_godot = [3, 2, 0] # Used for doing file manipulation stuff so as to not keep making File instances. @@ -202,6 +202,32 @@ func is_version_ok(engine_info=Engine.get_version_info(),required=req_godot): return nvl(is_ok, true) +func godot_version(engine_info=Engine.get_version_info()): + return str(engine_info.major, '.', engine_info.minor, '.', engine_info.patch) + + +func is_godot_version(expected, engine_info=Engine.get_version_info()): + var engine_array = [engine_info.major, engine_info.minor, engine_info.patch] + var expected_array = expected.split('.') + + if(expected_array.size() > engine_array.size()): + return false + + var is_version = true + var i = 0 + while(i < expected_array.size() and i < engine_array.size() and is_version): + if(expected_array[i] == str(engine_array[i])): + i += 1 + else: + is_version = false + + return is_version + + +func is_godot_version_gte(expected, engine_info=Engine.get_version_info()): + return is_version_ok(engine_info, expected.split('.')) + + # ------------------------------------------------------------------------------ # Everything should get a logger through this. # |
