From 5828df43e2902b0d444672d334955667e6e61f21 Mon Sep 17 00:00:00 2001 From: Fabien Freling Date: Mon, 24 Feb 2020 23:08:03 +0100 Subject: [PATCH] remove unit test framework --- addons/gut/GutScene.gd | 348 --------- addons/gut/GutScene.tscn | 306 -------- addons/gut/LICENSE.md | 22 - addons/gut/doubler.gd | 487 ------------ addons/gut/gut.gd | 1334 -------------------------------- addons/gut/gut_cmdln.gd | 356 --------- addons/gut/gut_plugin.gd | 12 - addons/gut/hook_script.gd | 35 - addons/gut/icon.png | Bin 320 -> 0 bytes addons/gut/icon.png.import | 34 - addons/gut/logger.gd | 105 --- addons/gut/method_maker.gd | 200 ----- addons/gut/one_to_many.gd | 38 - addons/gut/optparse.gd | 250 ------ addons/gut/plugin.cfg | 7 - addons/gut/signal_watcher.gd | 166 ---- addons/gut/source_code_pro.fnt | Bin 26499 -> 0 bytes addons/gut/spy.gd | 96 --- addons/gut/stub_params.gd | 43 - addons/gut/stubber.gd | 162 ---- addons/gut/summary.gd | 153 ---- addons/gut/test.gd | 1088 -------------------------- addons/gut/test_collector.gd | 241 ------ addons/gut/thing_counter.gd | 43 - addons/gut/utils.gd | 122 --- justfile | 3 - project.godot | 4 - test/tests.tscn | 38 - test/unit/test_example.gd | 27 - 29 files changed, 5720 deletions(-) delete mode 100644 addons/gut/GutScene.gd delete mode 100644 addons/gut/GutScene.tscn delete mode 100644 addons/gut/LICENSE.md delete mode 100644 addons/gut/doubler.gd delete mode 100644 addons/gut/gut.gd delete mode 100644 addons/gut/gut_cmdln.gd delete mode 100644 addons/gut/gut_plugin.gd delete mode 100644 addons/gut/hook_script.gd delete mode 100644 addons/gut/icon.png delete mode 100644 addons/gut/icon.png.import delete mode 100644 addons/gut/logger.gd delete mode 100644 addons/gut/method_maker.gd delete mode 100644 addons/gut/one_to_many.gd delete mode 100644 addons/gut/optparse.gd delete mode 100644 addons/gut/plugin.cfg delete mode 100644 addons/gut/signal_watcher.gd delete mode 100644 addons/gut/source_code_pro.fnt delete mode 100644 addons/gut/spy.gd delete mode 100644 addons/gut/stub_params.gd delete mode 100644 addons/gut/stubber.gd delete mode 100644 addons/gut/summary.gd delete mode 100644 addons/gut/test.gd delete mode 100644 addons/gut/test_collector.gd delete mode 100644 addons/gut/thing_counter.gd delete mode 100644 addons/gut/utils.gd delete mode 100644 test/tests.tscn delete mode 100644 test/unit/test_example.gd diff --git a/addons/gut/GutScene.gd b/addons/gut/GutScene.gd deleted file mode 100644 index a67ed79..0000000 --- a/addons/gut/GutScene.gd +++ /dev/null @@ -1,348 +0,0 @@ -extends Panel - -onready var _script_list = $ScriptsList -onready var _nav = { - prev = $Navigation/Previous, - next = $Navigation/Next, - run = $Navigation/Run, - current_script = $Navigation/CurrentScript, - show_scripts = $Navigation/ShowScripts -} -onready var _progress = { - script = $ScriptProgress, - test = $TestProgress -} -onready var _summary = { - failing = $Summary/Failing, - passing = $Summary/Passing -} - -onready var _extras = $ExtraOptions -onready var _ignore_pauses = $ExtraOptions/IgnorePause -onready var _continue_button = $Continue/Continue -onready var _text_box = $TextDisplay/RichTextLabel - -onready var _titlebar = { - bar = $TitleBar, - time = $TitleBar/Time, - label = $TitleBar/Title -} - -var _mouse = { - down = false, - in_title = false, - down_pos = null, - in_handle = false -} -var _is_running = false -var _start_time = 0.0 -var _time = 0.0 - -const DEFAULT_TITLE = 'Gut: The Godot Unit Testing tool.' -var _utils = load('res://addons/gut/utils.gd').new() -var _text_box_blocker_enabled = true -var _pre_maximize_size = null - -signal end_pause -signal ignore_pause -signal log_level_changed -signal run_script -signal run_single_script -signal script_selected - -func _ready(): - _pre_maximize_size = rect_size - _hide_scripts() - _update_controls() - _nav.current_script.set_text("No scripts available") - set_title() - clear_summary() - $TitleBar/Time.set_text("") - $ExtraOptions/DisableBlocker.pressed = !_text_box_blocker_enabled - _extras.visible = false - update() - -func _process(delta): - if(_is_running): - _time = OS.get_unix_time() - _start_time - var disp_time = round(_time * 100)/100 - $TitleBar/Time.set_text(str(disp_time)) - -func _draw(): # needs get_size() - # Draw the lines in the corner to show where you can - # drag to resize the dialog - var grab_margin = 3 - var line_space = 3 - var grab_line_color = Color(.4, .4, .4) - for i in range(1, 10): - var x = rect_size - Vector2(i * line_space, grab_margin) - var y = rect_size - Vector2(grab_margin, i * line_space) - draw_line(x, y, grab_line_color, 1, true) - -func _on_Maximize_draw(): - # draw the maximize square thing. - var btn = $TitleBar/Maximize - btn.set_text('') - var w = btn.get_size().x - var h = btn.get_size().y - btn.draw_rect(Rect2(0, 0, w, h), Color(0, 0, 0, 1)) - btn.draw_rect(Rect2(2, 4, w - 4, h - 6), Color(1,1,1,1)) - -func _on_ShowExtras_draw(): - var btn = $Continue/ShowExtras - btn.set_text('') - var start_x = 20 - var start_y = 15 - var pad = 5 - var color = Color(.1, .1, .1, 1) - var width = 2 - for i in range(3): - var y = start_y + pad * i - btn.draw_line(Vector2(start_x, y), Vector2(btn.get_size().x - start_x, y), color, width, true) - -# #################### -# GUI Events -# #################### -func _on_Run_pressed(): - _run_mode() - emit_signal('run_script', get_selected_index()) - -func _on_CurrentScript_pressed(): - _run_mode() - emit_signal('run_single_script', get_selected_index()) - -func _on_Previous_pressed(): - _select_script(get_selected_index() - 1) - -func _on_Next_pressed(): - _select_script(get_selected_index() + 1) - -func _on_LogLevelSlider_value_changed(value): - emit_signal('log_level_changed', $LogLevelSlider.value) - -func _on_Continue_pressed(): - _continue_button.disabled = true - emit_signal('end_pause') - -func _on_IgnorePause_pressed(): - var checked = _ignore_pauses.is_pressed() - emit_signal('ignore_pause', checked) - if(checked): - emit_signal('end_pause') - _continue_button.disabled = true - -func _on_ShowScripts_pressed(): - _toggle_scripts() - -func _on_ScriptsList_item_selected(index): - _select_script(index) - -func _on_TitleBar_mouse_entered(): - _mouse.in_title = true - -func _on_TitleBar_mouse_exited(): - _mouse.in_title = false - -func _input(event): - if(event is InputEventMouseButton): - if(event.button_index == 1): - _mouse.down = event.pressed - if(_mouse.down): - _mouse.down_pos = event.position - - if(_mouse.in_title): - if(event is InputEventMouseMotion and _mouse.down): - set_position(get_position() + (event.position - _mouse.down_pos)) - _mouse.down_pos = event.position - - if(_mouse.in_handle): - if(event is InputEventMouseMotion and _mouse.down): - var new_size = rect_size + event.position - _mouse.down_pos - var new_mouse_down_pos = event.position - rect_size = new_size - _mouse.down_pos = new_mouse_down_pos - _pre_maximize_size = rect_size - -func _on_ResizeHandle_mouse_entered(): - _mouse.in_handle = true - -func _on_ResizeHandle_mouse_exited(): - _mouse.in_handle = false - -# Send scroll type events through to the text box -func _on_FocusBlocker_gui_input(ev): - if(_text_box_blocker_enabled): - if(ev is InputEventPanGesture): - get_text_box()._gui_input(ev) - # convert a drag into a pan gesture so it scrolls. - elif(ev is InputEventScreenDrag): - var converted = InputEventPanGesture.new() - converted.delta = Vector2(0, ev.relative.y) - converted.position = Vector2(0, 0) - get_text_box()._gui_input(converted) - elif(ev is InputEventMouseButton and (ev.button_index == BUTTON_WHEEL_DOWN or ev.button_index == BUTTON_WHEEL_UP)): - get_text_box()._gui_input(ev) - else: - get_text_box()._gui_input(ev) - print(ev) - -func _on_RichTextLabel_gui_input(ev): - pass - # leaving this b/c it is wired up and might have to send - # more signals through - print(ev) - -func _on_Copy_pressed(): - _text_box.select_all() - _text_box.copy() - _text_box.deselect() - -func _on_DisableBlocker_toggled(button_pressed): - _text_box_blocker_enabled = !button_pressed - -func _on_ShowExtras_toggled(button_pressed): - _extras.visible = button_pressed - -func _on_Maximize_pressed(): - if(rect_size == _pre_maximize_size): - maximize() - else: - rect_size = _pre_maximize_size -# #################### -# Private -# #################### -func _run_mode(is_running=true): - if(is_running): - _start_time = OS.get_unix_time() - _time = _start_time - _summary.failing.set_text("0") - _summary.passing.set_text("0") - _is_running = is_running - - _hide_scripts() - var ctrls = $Navigation.get_children() - for i in range(ctrls.size()): - ctrls[i].disabled = is_running - -func _select_script(index): - $Navigation/CurrentScript.set_text(_script_list.get_item_text(index)) - _script_list.select(index) - _update_controls() - -func _toggle_scripts(): - if(_script_list.visible): - _hide_scripts() - else: - _show_scripts() - -func _show_scripts(): - _script_list.show() - -func _hide_scripts(): - _script_list.hide() - -func _update_controls(): - var is_empty = _script_list.get_selected_items().size() == 0 - if(is_empty): - _nav.next.disabled = true - _nav.prev.disabled = true - else: - var index = get_selected_index() - _nav.prev.disabled = index <= 0 - _nav.next.disabled = index >= _script_list.get_item_count() - 1 - - _nav.run.disabled = is_empty - _nav.current_script.disabled = is_empty - _nav.show_scripts.disabled = is_empty - - -# #################### -# Public -# #################### -func run_mode(is_running=true): - _run_mode(is_running) - -func set_scripts(scripts): - _script_list.clear() - for i in range(scripts.size()): - _script_list.add_item(scripts[i]) - _select_script(0) - _update_controls() - -func select_script(index): - _select_script(index) - -func get_selected_index(): - return _script_list.get_selected_items()[0] - -func get_log_level(): - return $LogLevelSlider.value - -func set_log_level(value): - $LogLevelSlider.value = _utils.nvl(value, 0) - -func set_ignore_pause(should): - _ignore_pauses.pressed = should - -func get_ignore_pause(): - return _ignore_pauses.pressed - -func get_text_box(): - return $TextDisplay/RichTextLabel - -func end_run(): - _run_mode(false) - _update_controls() - -func set_progress_script_max(value): - _progress.script.set_max(value) - -func set_progress_script_value(value): - _progress.script.set_value(value) - -func set_progress_test_max(value): - _progress.test.set_max(value) - -func set_progress_test_value(value): - _progress.test.set_value(value) - -func clear_progress(): - _progress.test.set_value(0) - _progress.script.set_value(0) - -func pause(): - print('we got here') - _continue_button.disabled = false - -func set_title(title=null): - if(title == null): - $TitleBar/Title.set_text(DEFAULT_TITLE) - else: - $TitleBar/Title.set_text(title) - -func get_run_duration(): - return $TitleBar/Time.text.to_float() - -func add_passing(amount=1): - if(!_summary): - return - _summary.passing.set_text(str(_summary.passing.get_text().to_int() + amount)) - $Summary.show() - -func add_failing(amount=1): - if(!_summary): - return - _summary.failing.set_text(str(_summary.failing.get_text().to_int() + amount)) - $Summary.show() - -func clear_summary(): - _summary.passing.set_text("0") - _summary.failing.set_text("0") - $Summary.hide() - -func maximize(): - if(is_inside_tree()): - var vp_size_offset = get_viewport().size - rect_size = vp_size_offset / get_scale() - set_position(Vector2(0, 0)) - diff --git a/addons/gut/GutScene.tscn b/addons/gut/GutScene.tscn deleted file mode 100644 index 3db50b8..0000000 --- a/addons/gut/GutScene.tscn +++ /dev/null @@ -1,306 +0,0 @@ -[gd_scene load_steps=5 format=2] - -[ext_resource path="res://addons/gut/GutScene.gd" type="Script" id=1] - -[sub_resource type="StyleBoxFlat" id=1] -bg_color = Color( 0.193863, 0.205501, 0.214844, 1 ) -corner_radius_top_left = 20 -corner_radius_top_right = 20 - -[sub_resource type="StyleBoxFlat" id=2] -bg_color = Color( 1, 1, 1, 1 ) -border_color = Color( 0, 0, 0, 1 ) -corner_radius_top_left = 5 -corner_radius_top_right = 5 - -[sub_resource type="Theme" id=3] -resource_local_to_scene = true -Panel/styles/panel = SubResource( 2 ) -Panel/styles/panelf = null -Panel/styles/panelnc = null - -[node name="Gut" type="Panel"] -margin_right = 740.0 -margin_bottom = 320.0 -rect_min_size = Vector2( 740, 250 ) -custom_styles/panel = SubResource( 1 ) -script = ExtResource( 1 ) - -[node name="TitleBar" type="Panel" parent="."] -anchor_right = 1.0 -margin_bottom = 40.0 -theme = SubResource( 3 ) - -[node name="Title" type="Label" parent="TitleBar"] -anchor_right = 1.0 -margin_bottom = 40.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "Gut" -align = 1 -valign = 1 - -[node name="Time" type="Label" parent="TitleBar"] -anchor_left = 1.0 -anchor_right = 1.0 -margin_left = -114.0 -margin_right = -53.0 -margin_bottom = 40.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "9999.99" -valign = 1 - -[node name="Maximize" type="Button" parent="TitleBar"] -anchor_left = 1.0 -anchor_right = 1.0 -margin_left = -30.0 -margin_top = 10.0 -margin_right = -6.0 -margin_bottom = 30.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "M" -flat = true - -[node name="ScriptProgress" type="ProgressBar" parent="."] -editor/display_folded = true -anchor_top = 1.0 -anchor_bottom = 1.0 -margin_left = 70.0 -margin_top = -100.0 -margin_right = 180.0 -margin_bottom = -70.0 -step = 1.0 - -[node name="Label" type="Label" parent="ScriptProgress"] -margin_left = -70.0 -margin_right = -10.0 -margin_bottom = 24.0 -text = "Script" -align = 1 -valign = 1 - -[node name="TestProgress" type="ProgressBar" parent="."] -editor/display_folded = true -anchor_top = 1.0 -anchor_bottom = 1.0 -margin_left = 70.0 -margin_top = -70.0 -margin_right = 180.0 -margin_bottom = -40.0 -step = 1.0 - -[node name="Label" type="Label" parent="TestProgress"] -margin_left = -70.0 -margin_right = -10.0 -margin_bottom = 24.0 -text = "Tests" -align = 1 -valign = 1 - -[node name="TextDisplay" type="Panel" parent="."] -editor/display_folded = true -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_top = 40.0 -margin_bottom = -107.0 -__meta__ = { -"_edit_group_": true -} - -[node name="RichTextLabel" type="TextEdit" parent="TextDisplay"] -anchor_right = 1.0 -anchor_bottom = 1.0 -mouse_default_cursor_shape = 0 -readonly = true -syntax_highlighting = true -smooth_scrolling = true - -[node name="FocusBlocker" type="Panel" parent="TextDisplay"] -self_modulate = Color( 1, 1, 1, 0 ) -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_right = -10.0 - -[node name="Navigation" type="Panel" parent="."] -editor/display_folded = true -self_modulate = Color( 1, 1, 1, 0 ) -anchor_top = 1.0 -anchor_bottom = 1.0 -margin_left = 220.0 -margin_top = -100.0 -margin_right = 580.0 - -[node name="Previous" type="Button" parent="Navigation"] -margin_left = -30.0 -margin_right = 50.0 -margin_bottom = 40.0 -text = "<" - -[node name="Next" type="Button" parent="Navigation"] -margin_left = 230.0 -margin_right = 310.0 -margin_bottom = 40.0 -text = ">" - -[node name="Run" type="Button" parent="Navigation"] -margin_left = 60.0 -margin_right = 220.0 -margin_bottom = 40.0 -text = "Run" - -[node name="CurrentScript" type="Button" parent="Navigation"] -margin_left = -30.0 -margin_top = 50.0 -margin_right = 310.0 -margin_bottom = 90.0 -text = "res://test/unit/test_gut.gd" -clip_text = true - -[node name="ShowScripts" type="Button" parent="Navigation"] -margin_left = 320.0 -margin_top = 50.0 -margin_right = 360.0 -margin_bottom = 90.0 -text = "..." - -[node name="LogLevelSlider" type="HSlider" parent="."] -editor/display_folded = true -anchor_top = 1.0 -anchor_bottom = 1.0 -margin_left = 80.0 -margin_top = -40.0 -margin_right = 130.0 -margin_bottom = -20.0 -rect_scale = Vector2( 2, 2 ) -max_value = 2.0 -tick_count = 3 -ticks_on_borders = true - -[node name="Label" type="Label" parent="LogLevelSlider"] -margin_left = -35.0 -margin_top = 5.0 -margin_right = 25.0 -margin_bottom = 25.0 -rect_scale = Vector2( 0.5, 0.5 ) -text = "Log Level" -align = 1 -valign = 1 - -[node name="ScriptsList" type="ItemList" parent="."] -anchor_bottom = 1.0 -margin_left = 180.0 -margin_top = 40.0 -margin_right = 620.0 -margin_bottom = -108.0 -allow_reselect = true - -[node name="ExtraOptions" type="Panel" parent="."] -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = -210.0 -margin_top = -246.0 -margin_bottom = -106.0 -custom_styles/panel = SubResource( 1 ) - -[node name="IgnorePause" type="CheckBox" parent="ExtraOptions"] -margin_left = 10.0 -margin_top = 10.0 -margin_right = 128.0 -margin_bottom = 34.0 -rect_scale = Vector2( 1.5, 1.5 ) -text = "Ignore Pauses" - -[node name="DisableBlocker" type="CheckBox" parent="ExtraOptions"] -margin_left = 10.0 -margin_top = 50.0 -margin_right = 130.0 -margin_bottom = 74.0 -rect_scale = Vector2( 1.5, 1.5 ) -text = "Selectable" - -[node name="Copy" type="Button" parent="ExtraOptions"] -margin_left = 20.0 -margin_top = 90.0 -margin_right = 200.0 -margin_bottom = 130.0 -text = "Copy" - -[node name="ResizeHandle" type="Control" parent="."] -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = -40.0 -margin_top = -40.0 - -[node name="Continue" type="Panel" parent="."] -self_modulate = Color( 1, 1, 1, 0 ) -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = -150.0 -margin_top = -100.0 -margin_right = -30.0 -margin_bottom = -10.0 - -[node name="Continue" type="Button" parent="Continue"] -margin_top = 50.0 -margin_right = 119.0 -margin_bottom = 90.0 -disabled = true -text = "Continue" - -[node name="ShowExtras" type="Button" parent="Continue"] -margin_left = 50.0 -margin_right = 120.0 -margin_bottom = 40.0 -rect_pivot_offset = Vector2( 35, 20 ) -toggle_mode = true -text = "_" - -[node name="Summary" type="Node2D" parent="."] -editor/display_folded = true -position = Vector2( 0, 3 ) - -[node name="Passing" type="Label" parent="Summary"] -margin_top = 10.0 -margin_right = 40.0 -margin_bottom = 24.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "0" -align = 1 -valign = 1 - -[node name="Failing" type="Label" parent="Summary"] -margin_left = 40.0 -margin_top = 10.0 -margin_right = 80.0 -margin_bottom = 24.0 -custom_colors/font_color = Color( 0, 0, 0, 1 ) -text = "0" -align = 1 -valign = 1 - -[connection signal="mouse_entered" from="TitleBar" to="." method="_on_TitleBar_mouse_entered"] -[connection signal="mouse_exited" from="TitleBar" to="." method="_on_TitleBar_mouse_exited"] -[connection signal="draw" from="TitleBar/Maximize" to="." method="_on_Maximize_draw"] -[connection signal="pressed" from="TitleBar/Maximize" to="." method="_on_Maximize_pressed"] -[connection signal="gui_input" from="TextDisplay/RichTextLabel" to="." method="_on_RichTextLabel_gui_input"] -[connection signal="gui_input" from="TextDisplay/FocusBlocker" to="." method="_on_FocusBlocker_gui_input"] -[connection signal="pressed" from="Navigation/Previous" to="." method="_on_Previous_pressed"] -[connection signal="pressed" from="Navigation/Next" to="." method="_on_Next_pressed"] -[connection signal="pressed" from="Navigation/Run" to="." method="_on_Run_pressed"] -[connection signal="pressed" from="Navigation/CurrentScript" to="." method="_on_CurrentScript_pressed"] -[connection signal="pressed" from="Navigation/ShowScripts" to="." method="_on_ShowScripts_pressed"] -[connection signal="value_changed" from="LogLevelSlider" to="." method="_on_LogLevelSlider_value_changed"] -[connection signal="item_selected" from="ScriptsList" to="." method="_on_ScriptsList_item_selected"] -[connection signal="pressed" from="ExtraOptions/IgnorePause" to="." method="_on_IgnorePause_pressed"] -[connection signal="toggled" from="ExtraOptions/DisableBlocker" to="." method="_on_DisableBlocker_toggled"] -[connection signal="pressed" from="ExtraOptions/Copy" to="." method="_on_Copy_pressed"] -[connection signal="mouse_entered" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_entered"] -[connection signal="mouse_exited" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_exited"] -[connection signal="pressed" from="Continue/Continue" to="." method="_on_Continue_pressed"] -[connection signal="draw" from="Continue/ShowExtras" to="." method="_on_ShowExtras_draw"] -[connection signal="toggled" from="Continue/ShowExtras" to="." method="_on_ShowExtras_toggled"] diff --git a/addons/gut/LICENSE.md b/addons/gut/LICENSE.md deleted file mode 100644 index a38ac23..0000000 --- a/addons/gut/LICENSE.md +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) -===================== - -Copyright (c) 2018 Tom "Butch" Wesley - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/addons/gut/doubler.gd b/addons/gut/doubler.gd deleted file mode 100644 index 7641150..0000000 --- a/addons/gut/doubler.gd +++ /dev/null @@ -1,487 +0,0 @@ -# ------------------------------------------------------------------------------ -# Utility class to hold the local and built in methods separately. Add all local -# methods FIRST, then add built ins. -# ------------------------------------------------------------------------------ -class ScriptMethods: - # List of methods that should not be overloaded when they are not defined - # in the class being doubled. These either break things if they are - # overloaded or do not have a "super" equivalent so we can't just pass - # through. - var _blacklist = [ - 'has_method', - 'get_script', - 'get', - '_notification', - 'get_path', - '_enter_tree', - '_exit_tree', - '_process', - '_draw', - '_physics_process', - '_input', - '_unhandled_input', - '_unhandled_key_input', - '_set', - '_get', # probably - 'emit_signal', # can't handle extra parameters to be sent with signal. - 'draw_mesh', # issue with one parameter, value is `Null((..), (..), (..))`` - '_to_string', # nonexistant function ._to_string - ] - - var built_ins = [] - var local_methods = [] - var _method_names = [] - - func is_blacklisted(method_meta): - return _blacklist.find(method_meta.name) != -1 - - func _add_name_if_does_not_have(method_name): - var should_add = _method_names.find(method_name) == -1 - if(should_add): - _method_names.append(method_name) - return should_add - - func add_built_in_method(method_meta): - var did_add = _add_name_if_does_not_have(method_meta.name) - if(did_add and !is_blacklisted(method_meta)): - built_ins.append(method_meta) - - func add_local_method(method_meta): - var did_add = _add_name_if_does_not_have(method_meta.name) - if(did_add): - local_methods.append(method_meta) - - func to_s(): - var text = "Locals\n" - for i in range(local_methods.size()): - text += str(" ", local_methods[i].name, "\n") - text += "Built-Ins\n" - for i in range(built_ins.size()): - text += str(" ", built_ins[i].name, "\n") - return text - -# ------------------------------------------------------------------------------ -# Helper class to deal with objects and inner classes. -# ------------------------------------------------------------------------------ -class ObjectInfo: - var _path = null - var _subpaths = [] - var _utils = load('res://addons/gut/utils.gd').new() - var _method_strategy = null - var make_partial_double = false - var scene_path = null - var _native_class = null - var _native_class_instance = null - - func _init(path, subpath=null): - _path = path - if(subpath != null): - _subpaths = _utils.split_string(subpath, '/') - - # Returns an instance of the class/inner class - func instantiate(): - var to_return = null - if(is_native()): - to_return = _native_class.new() - else: - to_return = get_loaded_class().new() - return to_return - - # Can't call it get_class because that is reserved so it gets this ugly name. - # Loads up the class and then any inner classes to give back a reference to - # the desired Inner class (if there is any) - func get_loaded_class(): - var LoadedClass = load(_path) - for i in range(_subpaths.size()): - LoadedClass = LoadedClass.get(_subpaths[i]) - return LoadedClass - - func to_s(): - return str(_path, '[', get_subpath(), ']') - - func get_path(): - return _path - - func get_subpath(): - return _utils.join_array(_subpaths, '/') - - func has_subpath(): - return _subpaths.size() != 0 - - func get_extends_text(): - var extend = null - if(is_native()): - extend = str("extends ", get_native_class_name()) - else: - extend = str("extends '", get_path(), '\'') - - if(has_subpath()): - extend += str('.', get_subpath().replace('/', '.')) - - return extend - - func get_method_strategy(): - return _method_strategy - - func set_method_strategy(method_strategy): - _method_strategy = method_strategy - - func is_native(): - return _native_class != null - - func set_native_class(native_class): - _native_class = native_class - _native_class_instance = native_class.new() - _path = _native_class_instance.get_class() - - func get_native_class_name(): - return _native_class_instance.get_class() - -# ------------------------------------------------------------------------------ -# START Doubler -# ------------------------------------------------------------------------------ -var _utils = load('res://addons/gut/utils.gd').new() - -var _output_dir = null -var _double_count = 0 # used in making files names unique -var _use_unique_names = true -var _spy = null -var _ignored_methods = _utils.OneToMany.new() - -var _stubber = _utils.Stubber.new() -var _lgr = _utils.get_logger() -var _method_maker = _utils.MethodMaker.new() -var _strategy = null - - -func _init(strategy=_utils.DOUBLE_STRATEGY.PARTIAL): - # make sure _method_maker gets logger too - set_logger(_utils.get_logger()) - _strategy = strategy - -# ############### -# Private -# ############### -func _get_indented_line(indents, text): - var to_return = '' - for i in range(indents): - to_return += "\t" - return str(to_return, text, "\n") - - -func _stub_to_call_super(obj_info, method_name): - var path = obj_info.get_path() - if(obj_info.scene_path != null): - path = obj_info.scene_path - var params = _utils.StubParams.new(path, method_name, obj_info.get_subpath()) - params.to_call_super() - _stubber.add_stub(params) - - -func _write_file(obj_info, dest_path, override_path=null): - var script_methods = _get_methods(obj_info) - - var metadata = _get_stubber_metadata_text(obj_info) - if(override_path): - metadata = _get_stubber_metadata_text(obj_info, override_path) - - var f = File.new() - var f_result = f.open(dest_path, f.WRITE) - - if(f_result != OK): - print('Error creating file ', dest_path) - print('Could not create double for :', obj_info.to_s()) - return - - f.store_string(str(obj_info.get_extends_text(), "\n")) - f.store_string(metadata) - - for i in range(script_methods.local_methods.size()): - if(obj_info.make_partial_double): - _stub_to_call_super(obj_info, script_methods.local_methods[i].name) - f.store_string(_get_func_text(script_methods.local_methods[i])) - - for i in range(script_methods.built_ins.size()): - _stub_to_call_super(obj_info, script_methods.built_ins[i].name) - f.store_string(_get_func_text(script_methods.built_ins[i])) - - f.close() - - -func _double_scene_and_script(scene_info, dest_path): - var dir = Directory.new() - dir.copy(scene_info.get_path(), dest_path) - - var inst = load(scene_info.get_path()).instance() - var script_path = null - if(inst.get_script()): - script_path = inst.get_script().get_path() - inst.free() - - if(script_path): - var oi = ObjectInfo.new(script_path) - oi.set_method_strategy(scene_info.get_method_strategy()) - oi.make_partial_double = scene_info.make_partial_double - oi.scene_path = scene_info.get_path() - var double_path = _double(oi, scene_info.get_path()) - var dq = '"' - - var f = File.new() - f.open(dest_path, f.READ) - var source = f.get_as_text() - f.close() - - source = source.replace(dq + script_path + dq, dq + double_path + dq) - - f.open(dest_path, f.WRITE) - f.store_string(source) - f.close() - - return script_path - -func _get_methods(object_info): - var obj = object_info.instantiate() - # any method in the script or super script - var script_methods = ScriptMethods.new() - var methods = obj.get_method_list() - - # first pass is for local methods only - for i in range(methods.size()): - # 65 is a magic number for methods in script, though documentation - # says 64. This picks up local overloads of base class methods too. - if(methods[i].flags == 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): - script_methods.add_local_method(methods[i]) - - - if(object_info.get_method_strategy() == _utils.DOUBLE_STRATEGY.FULL): - # second pass is for anything not local - for i in range(methods.size()): - # 65 is a magic number for methods in script, though documentation - # says 64. This picks up local overloads of base class methods too. - if(methods[i].flags != 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): - script_methods.add_built_in_method(methods[i]) - - return script_methods - -func _get_inst_id_ref_str(inst): - var ref_str = 'null' - if(inst): - ref_str = str('instance_from_id(', inst.get_instance_id(),')') - return ref_str - -func _get_stubber_metadata_text(obj_info, override_path = null): - var path = obj_info.get_path() - if(override_path != null): - path = override_path - return "var __gut_metadata_ = {\n" + \ - "\tpath='" + path + "',\n" + \ - "\tsubpath='" + obj_info.get_subpath() + "',\n" + \ - "\tstubber=" + _get_inst_id_ref_str(_stubber) + ",\n" + \ - "\tspy=" + _get_inst_id_ref_str(_spy) + "\n" + \ - "}\n" - -func _get_spy_text(method_hash): - var txt = '' - if(_spy): - var called_with = _method_maker.get_spy_call_parameters_text(method_hash) - txt += "\t__gut_metadata_.spy.add_call(self, '" + method_hash.name + "', " + called_with + ")\n" - return txt - -func _get_func_text(method_hash): - var ftxt = _method_maker.get_decleration_text(method_hash) + "\n" - - var called_with = _method_maker.get_spy_call_parameters_text(method_hash) - ftxt += _get_spy_text(method_hash) - - if(_stubber and method_hash.name != '_init'): - var call_method = _method_maker.get_super_call_text(method_hash) - ftxt += "\tif(__gut_metadata_.stubber.should_call_super(self, '" + method_hash.name + "', " + called_with + ")):\n" - ftxt += "\t\treturn " + call_method + "\n" - ftxt += "\telse:\n" - ftxt += "\t\treturn __gut_metadata_.stubber.get_return(self, '" + method_hash.name + "', " + called_with + ")\n" - else: - ftxt += "\tpass\n" - - return ftxt - -func _get_super_func_text(method_hash): - var call_method = _method_maker.get_super_call_text(method_hash) - - var call_super_text = str("return ", call_method, "\n") - - var ftxt = _method_maker.get_decleration_text(method_hash) + "\n" - ftxt += _get_spy_text(method_hash) - - ftxt += _get_indented_line(1, call_super_text) - - return ftxt - -# returns the path to write the double file to -func _get_temp_path(object_info): - var file_name = null - var extension = null - if(object_info.is_native()): - file_name = object_info.get_native_class_name() - extension = 'gd' - else: - file_name = object_info.get_path().get_file().get_basename() - extension = object_info.get_path().get_extension() - - if(object_info.has_subpath()): - file_name += '__' + object_info.get_subpath().replace('/', '__') - - if(_use_unique_names): - file_name += str('__dbl', _double_count, '__.', extension) - else: - file_name += '.' + extension - - var to_return = _output_dir.plus_file(file_name) - return to_return - -func _double(obj_info, override_path=null): - var temp_path = _get_temp_path(obj_info) - _write_file(obj_info, temp_path, override_path) - _double_count += 1 - return temp_path - - -func _double_script(path, make_partial, strategy): - var oi = ObjectInfo.new(path) - oi.make_partial_double = make_partial - oi.set_method_strategy(strategy) - var to_return = load(_double(oi)) - - return to_return - -func _double_inner(path, subpath, make_partial, strategy): - var oi = ObjectInfo.new(path, subpath) - oi.set_method_strategy(strategy) - oi.make_partial_double = make_partial - var to_return = load(_double(oi)) - - return to_return - -func _double_scene(path, make_partial, strategy): - var oi = ObjectInfo.new(path) - oi.set_method_strategy(strategy) - oi.make_partial_double = make_partial - var temp_path = _get_temp_path(oi) - _double_scene_and_script(oi, temp_path) - - return load(temp_path) - -func _double_gdnative(native_class, make_partial, strategy): - var oi = ObjectInfo.new(null) - oi.set_native_class(native_class) - oi.set_method_strategy(strategy) - oi.make_partial_double = make_partial - var to_return = load(_double(oi)) - - return to_return - - - -# ############### -# Public -# ############### -func get_output_dir(): - return _output_dir - -func set_output_dir(output_dir): - _output_dir = output_dir - var d = Directory.new() - d.make_dir_recursive(output_dir) - -func get_spy(): - return _spy - -func set_spy(spy): - _spy = spy - -func get_stubber(): - return _stubber - -func set_stubber(stubber): - _stubber = stubber - -func get_logger(): - return _lgr - -func set_logger(logger): - _lgr = logger - _method_maker.set_logger(logger) - -func get_strategy(): - return _strategy - -func set_strategy(strategy): - _strategy = strategy - -func partial_double_scene(path, strategy=_strategy): - return _double_scene(path, true, strategy) - -# double a scene -func double_scene(path, strategy=_strategy): - return _double_scene(path, false, strategy) - -# double a script/object -func double(path, strategy=_strategy): - return _double_script(path, false, strategy) - -func partial_double(path, strategy=_strategy): - return _double_script(path, true, strategy) - -func partial_double_inner(path, subpath, strategy=_strategy): - return _double_inner(path, subpath, true, strategy) - -# double an inner class in a script -func double_inner(path, subpath, strategy=_strategy): - return _double_inner(path, subpath, false, strategy) - -# must always use FULL strategy since this is a native class and you won't get -# any methods if you don't use FULL -func double_gdnative(native_class): - return _double_gdnative(native_class, false, _utils.DOUBLE_STRATEGY.FULL) - -# must always use FULL strategy since this is a native class and you won't get -# any methods if you don't use FULL -func partial_double_gdnative(native_class): - return _double_gdnative(native_class, true, _utils.DOUBLE_STRATEGY.FULL) - -func clear_output_directory(): - var did = false - if(_output_dir.find('user://') == 0): - var d = Directory.new() - var result = d.open(_output_dir) - # BIG GOTCHA HERE. If it cannot open the dir w/ erro 31, then the - # directory becomes res:// and things go on normally and gut clears out - # out res:// which is SUPER BAD. - if(result == OK): - d.list_dir_begin(true) - var f = d.get_next() - while(f != ''): - d.remove(f) - f = d.get_next() - did = true - return did - -func delete_output_directory(): - var did = clear_output_directory() - if(did): - var d = Directory.new() - d.remove(_output_dir) - -# When creating doubles a unique name is used that each double can be its own -# thing. Sometimes, for testing, we do not want to do this so this allows -# you to turn off creating unique names for each double class. -# -# THIS SHOULD NEVER BE USED OUTSIDE OF INTERNAL GUT TESTING. It can cause -# weird, hard to track down problems. -func set_use_unique_names(should): - _use_unique_names = should - -func add_ignored_method(path, method_name): - _ignored_methods.add(path, method_name) - -func get_ignored_methods(): - return _ignored_methods diff --git a/addons/gut/gut.gd b/addons/gut/gut.gd deleted file mode 100644 index 0ac0d02..0000000 --- a/addons/gut/gut.gd +++ /dev/null @@ -1,1334 +0,0 @@ -################################################################################ -#(G)odot (U)nit (T)est class -# -################################################################################ -#The MIT License (MIT) -#===================== -# -#Copyright (c) 2019 Tom "Butch" Wesley -# -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: -# -#The above copyright notice and this permission notice shall be included in -#all copies or substantial portions of the Software. -# -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -#THE SOFTWARE. -# -################################################################################ -# View readme for usage details. -# -# Version 6.8.1 -################################################################################ -#extends "res://addons/gut/gut_gui.gd" -tool -extends Control -var _version = '6.8.1' - -var _utils = load('res://addons/gut/utils.gd').new() -var _lgr = _utils.get_logger() -# Used to prevent multiple messages for deprecated setup/teardown messages -var _deprecated_tracker = _utils.ThingCounter.new() - -# ########################### -# Editor Variables -# ########################### -export(String) var _select_script = '' -export(String) var _tests_like = '' -export(String) var _inner_class_name = '' - -export var _run_on_load = false -export var _should_maximize = false setget set_should_maximize, get_should_maximize - -export var _should_print_to_console = true setget set_should_print_to_console, get_should_print_to_console -export(int, 'Failures only', 'Tests and failures', 'Everything') var _log_level = 1 setget set_log_level, get_log_level -# This var is JUST used to expose this setting in the editor -# the var that is used is in the _yield_between hash. -export var _yield_between_tests = true setget set_yield_between_tests, get_yield_between_tests -export var _disable_strict_datatype_checks = false setget disable_strict_datatype_checks, is_strict_datatype_checks_disabled -# The prefix used to get tests. -export var _test_prefix = 'test_' -export var _file_prefix = 'test_' -export var _file_extension = '.gd' -export var _inner_class_prefix = 'Test' - -export(String) var _temp_directory = 'user://gut_temp_directory' -export(String) var _export_path = '' setget set_export_path, get_export_path - -export var _include_subdirectories = false setget set_include_subdirectories, get_include_subdirectories -# Allow user to add test directories via editor. This is done with strings -# instead of an array because the interface for editing arrays is really -# cumbersome and complicates testing because arrays set through the editor -# apply to ALL instances. This also allows the user to use the built in -# dialog to pick a directory. -export(String, DIR) var _directory1 = '' -export(String, DIR) var _directory2 = '' -export(String, DIR) var _directory3 = '' -export(String, DIR) var _directory4 = '' -export(String, DIR) var _directory5 = '' -export(String, DIR) var _directory6 = '' -export(int, 'FULL', 'PARTIAL') var _double_strategy = _utils.DOUBLE_STRATEGY.PARTIAL setget set_double_strategy, get_double_strategy -export(String, FILE) var _pre_run_script = '' setget set_pre_run_script, get_pre_run_script -export(String, FILE) var _post_run_script = '' setget set_post_run_script, get_post_run_script -# The instance that is created from _pre_run_script. Accessible from -# get_pre_run_script_instance. -var _pre_run_script_instance = null -var _post_run_script_instance = null # This is not used except in tests. - -# ########################### -# Other Vars -# ########################### -const LOG_LEVEL_FAIL_ONLY = 0 -const LOG_LEVEL_TEST_AND_FAILURES = 1 -const LOG_LEVEL_ALL_ASSERTS = 2 -const WAITING_MESSAGE = '/# waiting #/' -const PAUSE_MESSAGE = '/# Pausing. Press continue button...#/' - -var _script_name = null -var _test_collector = _utils.TestCollector.new() - -# The instanced scripts. This is populated as the scripts are run. -var _test_script_objects = [] - -var _waiting = false -var _done = false -var _is_running = false - -var _current_test = null -var _log_text = "" - -var _pause_before_teardown = false -# when true _pause_before_teardown will be ignored. useful -# when batch processing and you don't want to watch. -var _ignore_pause_before_teardown = false -var _wait_timer = Timer.new() - -var _yield_between = { - should = false, - timer = Timer.new(), - after_x_tests = 5, - tests_since_last_yield = 0 -} - -var _was_yield_method_called = false -# used when yielding to gut instead of some other -# signal. Start with set_yield_time() -var _yield_timer = Timer.new() - -var _unit_test_name = '' -var _new_summary = null - -var _yielding_to = { - obj = null, - signal_name = '' -} - -var _stubber = _utils.Stubber.new() -var _doubler = _utils.Doubler.new() -var _spy = _utils.Spy.new() -var _gui = null - -const SIGNAL_TESTS_FINISHED = 'tests_finished' -const SIGNAL_STOP_YIELD_BEFORE_TEARDOWN = 'stop_yield_before_teardown' - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _init(): - # This min size has to be what the min size of the GutScene's min size is - # but it has to be set here and not inferred i think. - rect_min_size =Vector2(740, 250) - - add_user_signal(SIGNAL_TESTS_FINISHED) - add_user_signal(SIGNAL_STOP_YIELD_BEFORE_TEARDOWN) - add_user_signal('timeout') - add_user_signal('done_waiting') - _doubler.set_output_dir(_temp_directory) - _doubler.set_stubber(_stubber) - _doubler.set_spy(_spy) - _doubler.set_logger(_lgr) - _lgr.set_gut(self) - - _stubber.set_logger(_lgr) - _test_collector.set_logger(_lgr) - _gui = load('res://addons/gut/GutScene.tscn').instance() - -# ------------------------------------------------------------------------------ -# Initialize controls -# ------------------------------------------------------------------------------ -func _ready(): - _lgr.info(str('using [', OS.get_user_data_dir(), '] for temporary output.')) - - set_process_input(true) - - add_child(_wait_timer) - _wait_timer.set_wait_time(1) - _wait_timer.set_one_shot(true) - - add_child(_yield_between.timer) - _wait_timer.set_one_shot(true) - - add_child(_yield_timer) - _yield_timer.set_one_shot(true) - _yield_timer.connect('timeout', self, '_yielding_callback') - - _setup_gui() - - add_directory(_directory1) - add_directory(_directory2) - add_directory(_directory3) - add_directory(_directory4) - add_directory(_directory5) - add_directory(_directory6) - - if(_select_script != null): - select_script(_select_script) - - if(_tests_like != null): - set_unit_test_name(_tests_like) - - if(_run_on_load): - test_scripts(_select_script == null) - - if(_should_maximize): - maximize() - - # hide the panel that IS gut so that only the GUI is seen - self.self_modulate = Color(1,1,1,0) - show() - var v_info = Engine.get_version_info() - p(str('Godot version: ', v_info.major, '.', v_info.minor, '.', v_info.patch)) - p(str('GUT version: ', get_version())) - - -################################################################################ -# -# GUI Events and setup -# -################################################################################ -func _setup_gui(): - # This is how we get the size of the control to translate to the gui when - # the scene is run. This is also another reason why the min_rect_size - # must match between both gut and the gui. - _gui.rect_size = self.rect_size - add_child(_gui) - _gui.set_anchor(MARGIN_RIGHT, ANCHOR_END) - _gui.set_anchor(MARGIN_BOTTOM, ANCHOR_END) - _gui.connect('run_single_script', self, '_on_run_one') - _gui.connect('run_script', self, '_on_new_gui_run_script') - _gui.connect('end_pause', self, '_on_new_gui_end_pause') - _gui.connect('ignore_pause', self, '_on_new_gui_ignore_pause') - _gui.connect('log_level_changed', self, '_on_log_level_changed') - connect('tests_finished', _gui, 'end_run') - -func _add_scripts_to_gui(): - var scripts = [] - for i in range(_test_collector.scripts.size()): - var s = _test_collector.scripts[i] - var txt = '' - if(s.has_inner_class()): - txt = str(' - ', s.inner_class_name, ' (', s.tests.size(), ')') - else: - txt = str(s.get_full_name(), ' (', s.tests.size(), ')') - scripts.append(txt) - _gui.set_scripts(scripts) - -func _on_run_one(index): - clear_text() - var indexes = [index] - if(!_test_collector.scripts[index].has_inner_class()): - indexes = _get_indexes_matching_path(_test_collector.scripts[index].path) - _test_the_scripts(indexes) - -func _on_new_gui_run_script(index): - var indexes = [] - clear_text() - for i in range(index, _test_collector.scripts.size()): - indexes.append(i) - _test_the_scripts(indexes) - -func _on_new_gui_end_pause(): - _pause_before_teardown = false - emit_signal(SIGNAL_STOP_YIELD_BEFORE_TEARDOWN) - -func _on_new_gui_ignore_pause(should): - _ignore_pause_before_teardown = should - -func _on_log_level_changed(value): - _log_level = value - -##################### -# -# Events -# -##################### - -# ------------------------------------------------------------------------------ -# Timeout for the built in timer. emits the timeout signal. Start timer -# with set_yield_time() -# ------------------------------------------------------------------------------ -func _yielding_callback(from_obj=false): - if(_yielding_to.obj): - _yielding_to.obj.call_deferred( - "disconnect", - _yielding_to.signal_name, self, - '_yielding_callback') - _yielding_to.obj = null - _yielding_to.signal_name = '' - - if(from_obj): - # we must yiled for a little longer after the signal is emitted so that - # the signal can propagate to other objects. This was discovered trying - # to assert that obj/signal_name was emitted. Without this extra delay - # the yield returns and processing finishes before the rest of the - # objects can get the signal. This works b/c the timer will timeout - # and come back into this method but from_obj will be false. - _yield_timer.set_wait_time(.1) - _yield_timer.start() - else: - emit_signal('timeout') - -# ------------------------------------------------------------------------------ -# completed signal for GDScriptFucntionState returned from a test script that -# has yielded -# ------------------------------------------------------------------------------ -func _on_test_script_yield_completed(): - _waiting = false - -##################### -# -# Private -# -##################### - -# ------------------------------------------------------------------------------ -# Convert the _summary dictionary into text -# ------------------------------------------------------------------------------ -func _get_summary_text(): - var to_return = "\n\n*****************\nRun Summary\n*****************" - - to_return += "\n" + _new_summary.get_summary_text() + "\n" - - var logger_text = '' - if(_lgr.get_errors().size() > 0): - logger_text += str("\n * ", _lgr.get_errors().size(), ' Errors.') - if(_lgr.get_warnings().size() > 0): - logger_text += str("\n * ", _lgr.get_warnings().size(), ' Warnings.') - if(_lgr.get_deprecated().size() > 0): - logger_text += str("\n * ", _lgr.get_deprecated().size(), ' Deprecated calls.') - if(logger_text != ''): - logger_text = "\nWarnings/Errors:" + logger_text + "\n\n" - to_return += logger_text - - if(_new_summary.get_totals().tests > 0): - to_return += '+++ ' + str(_new_summary.get_totals().passing) + ' passed ' + str(_new_summary.get_totals().failing) + ' failed. ' + \ - "Tests finished in: " + str(_gui.get_run_duration()) + ' +++' - var c = Color(0, 1, 0) - if(_new_summary.get_totals().failing > 0): - c = Color(1, 0, 0) - elif(_new_summary.get_totals().pending > 0): - c = Color(1, 1, .8) - - _gui.get_text_box().add_color_region('+++', '+++', c) - else: - to_return += '+++ No tests ran +++' - _gui.get_text_box().add_color_region('+++', '+++', Color(1, 0, 0)) - - return to_return - -func _validate_hook_script(path): - var result = { - valid = true, - instance = null - } - - # empty path is valid but will have a null instance - if(path == ''): - return result - - var f = File.new() - if(f.file_exists(path)): - var inst = load(path).new() - if(inst and inst is _utils.HookScript): - result.instance = inst - result.valid = true - else: - result.valid = false - _lgr.error('The hook script [' + path + '] does not extend res://addons/gut/hook_script.gd') - else: - result.valid = false - _lgr.error('The hook script [' + path + '] does not exist.') - - return result - - -# ------------------------------------------------------------------------------ -# Runs a hook script. Script must exist, and must extend -# res://addons/gut/hook_script.gd -# ------------------------------------------------------------------------------ -func _run_hook_script(inst): - if(inst != null): - inst.gut = self - inst.run() - return inst - -# ------------------------------------------------------------------------------ -# Initialize variables for each run of a single test script. -# ------------------------------------------------------------------------------ -func _init_run(): - var valid = true - _test_collector.set_test_class_prefix(_inner_class_prefix) - _test_script_objects = [] - _new_summary = _utils.Summary.new() - - _log_text = "" - - _current_test = null - - _is_running = true - - _yield_between.tests_since_last_yield = 0 - - _gui.get_text_box().clear_colors() - _gui.get_text_box().add_keyword_color("PASSED", Color(0, 1, 0)) - _gui.get_text_box().add_keyword_color("FAILED", Color(1, 0, 0)) - _gui.get_text_box().add_color_region('/#', '#/', Color(.9, .6, 0)) - _gui.get_text_box().add_color_region('/-', '-/', Color(1, 1, 0)) - _gui.get_text_box().add_color_region('/*', '*/', Color(.5, .5, 1)) - - var pre_hook_result = _validate_hook_script(_pre_run_script) - _pre_run_script_instance = pre_hook_result.instance - var post_hook_result = _validate_hook_script(_post_run_script) - _post_run_script_instance = post_hook_result.instance - - valid = pre_hook_result.valid and post_hook_result.valid - - return valid - - - - -# ------------------------------------------------------------------------------ -# Print out run information and close out the run. -# ------------------------------------------------------------------------------ -func _end_run(): - var failed_tests = [] - var more_than_one = _test_script_objects.size() > 1 - - p(_get_summary_text(), 0) - p("\n") - if(!_utils.is_null_or_empty(_select_script)): - p('Ran Scripts matching ' + _select_script) - if(!_utils.is_null_or_empty(_unit_test_name)): - p('Ran Tests matching ' + _unit_test_name) - if(!_utils.is_null_or_empty(_inner_class_name)): - p('Ran Inner Classes matching ' + _inner_class_name) - - # For some reason the text edit control isn't scrolling to the bottom after - # the summary is printed. As a workaround, yield for a short time and - # then move the cursor. I found this workaround through trial and error. - _yield_between.timer.set_wait_time(0.1) - _yield_between.timer.start() - yield(_yield_between.timer, 'timeout') - _gui.get_text_box().cursor_set_line(_gui.get_text_box().get_line_count()) - - _is_running = false - update() - _run_hook_script(_post_run_script_instance) - emit_signal(SIGNAL_TESTS_FINISHED) - _gui.set_title("Finished. " + str(get_fail_count()) + " failures.") - -# ------------------------------------------------------------------------------ -# Checks the passed in thing to see if it is a "function state" object that gets -# returned when a function yields. -# ------------------------------------------------------------------------------ -func _is_function_state(script_result): - return script_result != null and \ - typeof(script_result) == TYPE_OBJECT and \ - script_result is GDScriptFunctionState - -# ------------------------------------------------------------------------------ -# Print out the heading for a new script -# ------------------------------------------------------------------------------ -func _print_script_heading(script): - if(_does_class_name_match(_inner_class_name, script.inner_class_name)): - p("\n/-----------------------------------------") - if(script.inner_class_name == null): - p("Running Script " + script.path, 0) - else: - p("Running Class [" + script.inner_class_name + "] in " + script.path, 0) - - if(!_utils.is_null_or_empty(_inner_class_name) and _does_class_name_match(_inner_class_name, script.inner_class_name)): - p(str(' [',script.inner_class_name, '] matches [', _inner_class_name, ']')) - - if(!_utils.is_null_or_empty(_unit_test_name)): - p(' Only running tests like: "' + _unit_test_name + '"') - - p("-----------------------------------------/") - -# ------------------------------------------------------------------------------ -# Just gets more logic out of _test_the_scripts. Decides if we should yield after -# this test based on flags and counters. -# ------------------------------------------------------------------------------ -func _should_yield_now(): - var should = _yield_between.should and \ - _yield_between.tests_since_last_yield == _yield_between.after_x_tests - if(should): - _yield_between.tests_since_last_yield = 0 - else: - _yield_between.tests_since_last_yield += 1 - return should - -# ------------------------------------------------------------------------------ -# Yes if the class name is null or the script's class name includes class_name -# ------------------------------------------------------------------------------ -func _does_class_name_match(the_class_name, script_class_name): - return (the_class_name == null or the_class_name == '') or (script_class_name != null and script_class_name.find(the_class_name) != -1) - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _setup_script(test_script): - test_script.gut = self - test_script.set_logger(_lgr) - add_child(test_script) - _test_script_objects.append(test_script) - - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _do_yield_between(time): - _yield_between.timer.set_wait_time(time) - _yield_between.timer.start() - return _yield_between.timer - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _wait_for_done(result): - var iter_counter = 0 - var print_after = 3 - - # sets waiting to false. - result.connect('completed', self, '_on_test_script_yield_completed') - - if(!_was_yield_method_called): - p('/# Yield detected, waiting #/') - - _was_yield_method_called = false - _waiting = true - _wait_timer.set_wait_time(0.25) - - while(_waiting): - iter_counter += 1 - if(iter_counter > print_after): - p(WAITING_MESSAGE, 2) - iter_counter = 0 - _wait_timer.start() - yield(_wait_timer, 'timeout') - - emit_signal('done_waiting') - -# ------------------------------------------------------------------------------ -# returns self so it can be integrated into the yield call. -# ------------------------------------------------------------------------------ -func _wait_for_continue_button(): - p(PAUSE_MESSAGE, 0) - _waiting = true - return self - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _call_deprecated_script_method(script, method, alt): - if(script.has_method(method)): - var txt = str(script, '-', method) - if(!_deprecated_tracker.has(txt)): - # Removing the deprecated line. I think it's still too early to - # start bothering people with this. Left everything here though - # because I don't want to remember how I did this last time. - #_lgr.deprecated(str('The method ', method, ' has been deprecated, use ', alt, ' instead.')) - _deprecated_tracker.add(txt) - script.call(method) - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _get_indexes_matching_script_name(name): - var indexes = [] # empty runs all - for i in range(_test_collector.scripts.size()): - if(_test_collector.scripts[i].get_filename().find(name) != -1): - indexes.append(i) - return indexes - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _get_indexes_matching_path(path): - var indexes = [] - for i in range(_test_collector.scripts.size()): - if(_test_collector.scripts[i].path == path): - indexes.append(i) - return indexes - -# ------------------------------------------------------------------------------ -# Run all tests in a script. This is the core logic for running tests. -# -# Note, this has to stay as a giant monstrosity of a method because of the -# yields. -# ------------------------------------------------------------------------------ -func _test_the_scripts(indexes=[]): - var is_valid = _init_run() - if(!is_valid): - _lgr.error('Something went wrong and the run was aborted.') - emit_signal(SIGNAL_TESTS_FINISHED) - return - - _run_hook_script(_pre_run_script_instance) - if(_pre_run_script_instance!= null and _pre_run_script_instance.should_abort()): - _lgr.error('pre-run abort') - emit_signal(SIGNAL_TESTS_FINISHED) - return - - _gui.run_mode() - - var indexes_to_run = [] - if(indexes.size()==0): - for i in range(_test_collector.scripts.size()): - indexes_to_run.append(i) - else: - indexes_to_run = indexes - - _gui.set_progress_script_max(indexes_to_run.size()) # New way - _gui.set_progress_script_value(0) - - var file = File.new() - if(_doubler.get_strategy() == _utils.DOUBLE_STRATEGY.FULL): - _lgr.info("Using Double Strategy FULL as default strategy. Keep an eye out for weirdness, this is still experimental.") - - # loop through scripts - for test_indexes in range(indexes_to_run.size()): - var the_script = _test_collector.scripts[indexes_to_run[test_indexes]] - - if(the_script.tests.size() > 0): - _gui.set_title('Running: ' + the_script.get_full_name()) - _print_script_heading(the_script) - _new_summary.add_script(the_script.get_full_name()) - - var test_script = the_script.get_new() - var script_result = null - _setup_script(test_script) - _doubler.set_strategy(_double_strategy) - - # yield between test scripts so things paint - if(_yield_between.should): - yield(_do_yield_between(0.01), 'timeout') - - # !!! - # Hack so there isn't another indent to this monster of a method. if - # inner class is set and we do not have a match then empty the tests - # for the current test. - # !!! - if(!_does_class_name_match(_inner_class_name, the_script.inner_class_name)): - the_script.tests = [] - else: - # call both pre-all-tests methods until prerun_setup is removed - _call_deprecated_script_method(test_script, 'prerun_setup', 'before_all') - test_script.before_all() - - _gui.set_progress_test_max(the_script.tests.size()) # New way - - # Each test in the script - for i in range(the_script.tests.size()): - _stubber.clear() - _spy.clear() - _doubler.clear_output_directory() - _current_test = the_script.tests[i] - - if((_unit_test_name != '' and _current_test.name.find(_unit_test_name) > -1) or - (_unit_test_name == '')): - p(_current_test.name, 1) - _new_summary.add_test(_current_test.name) - - # yield so things paint - if(_should_yield_now()): - yield(_do_yield_between(0.001), 'timeout') - - _call_deprecated_script_method(test_script, 'setup', 'before_each') - test_script.before_each() - - - #When the script yields it will return a GDScriptFunctionState object - script_result = test_script.call(_current_test.name) - if(_is_function_state(script_result)): - _wait_for_done(script_result) - yield(self, 'done_waiting') - - #if the test called pause_before_teardown then yield until - #the continue button is pressed. - if(_pause_before_teardown and !_ignore_pause_before_teardown): - _gui.pause() - yield(_wait_for_continue_button(), SIGNAL_STOP_YIELD_BEFORE_TEARDOWN) - - test_script.clear_signal_watcher() - - # call each post-each-test method until teardown is removed. - _call_deprecated_script_method(test_script, 'teardown', 'after_each') - test_script.after_each() - - if(_current_test.passed): - _gui.get_text_box().add_keyword_color(_current_test.name, Color(0, 1, 0)) - else: - _gui.get_text_box().add_keyword_color(_current_test.name, Color(1, 0, 0)) - - _gui.set_progress_test_value(i + 1) - _doubler.get_ignored_methods().clear() - - # call both post-all-tests methods until postrun_teardown is removed. - if(_does_class_name_match(_inner_class_name, the_script.inner_class_name)): - _call_deprecated_script_method(test_script, 'postrun_teardown', 'after_all') - test_script.after_all() - - # This might end up being very resource intensive if the scripts - # don't clean up after themselves. Might have to consolidate output - # into some other structure and kill the script objects with - # test_script.free() instead of remove child. - remove_child(test_script) - #END TESTS IN SCRIPT LOOP - _current_test = null - _gui.set_progress_script_value(test_indexes + 1) # new way - #END TEST SCRIPT LOOP - - _end_run() - -func _pass(text=''): - _gui.add_passing() - if(_current_test): - _new_summary.add_pass(_current_test.name, text) - -func _fail(text=''): - _gui.add_failing() - if(_current_test != null): - var line_text = ' at line ' + str(_extractLineNumber( _current_test)) - p(line_text, LOG_LEVEL_FAIL_ONLY) - # format for summary - line_text = "\n " + line_text - - _new_summary.add_fail(_current_test.name, text + line_text) - _current_test.passed = false - -# Extracts the line number from curren stacktrace by matching the test case name -func _extractLineNumber(current_test): - var line_number = current_test.line_number - # if stack trace available than extraxt the test case line number - var stackTrace = get_stack() - if(stackTrace!=null): - for index in stackTrace.size(): - var line = stackTrace[index] - var function = line.get("function") - if function == current_test.name: - line_number = line.get("line") - return line_number - -func _pending(text=''): - if(_current_test): - _new_summary.add_pending(_current_test.name, text) - -# Gets all the files in a directory and all subdirectories if get_include_subdirectories -# is true. The files returned are all sorted by name. -func _get_files(path, prefix, suffix): - var files = [] - var directories = [] - - var d = Directory.new() - d.open(path) - # true parameter tells list_dir_begin not to include "." and ".." directories. - d.list_dir_begin(true) - - # Traversing a directory is kinda odd. You have to start the process of listing - # the contents of a directory with list_dir_begin then use get_next until it - # returns an empty string. Then I guess you should end it. - var fs_item = d.get_next() - var full_path = '' - while(fs_item != ''): - full_path = path.plus_file(fs_item) - - #file_exists returns fasle for directories - if(d.file_exists(full_path)): - if(fs_item.begins_with(prefix) and fs_item.ends_with(suffix)): - files.append(full_path) - elif(get_include_subdirectories() and d.dir_exists(full_path)): - directories.append(full_path) - - fs_item = d.get_next() - d.list_dir_end() - - for dir in range(directories.size()): - var dir_files = _get_files(directories[dir], prefix, suffix) - for i in range(dir_files.size()): - files.append(dir_files[i]) - - files.sort() - return files -######################### -# -# public -# -######################### - -# ------------------------------------------------------------------------------ -# Conditionally prints the text to the console/results variable based on the -# current log level and what level is passed in. Whenever currently in a test, -# the text will be indented under the test. It can be further indented if -# desired. -# ------------------------------------------------------------------------------ -func p(text, level=0, indent=0): - var str_text = str(text) - var to_print = "" - var printing_test_name = false - - if(level <= _utils.nvl(_log_level, 0)): - if(_current_test != null): - #make sure everything printed during the execution - #of a test is at least indented once under the test - if(indent == 0): - indent = 1 - - #Print the name of the current test if we haven't - #printed it already. - if(!_current_test.has_printed_name): - to_print = "* " + _current_test.name - _current_test.has_printed_name = true - printing_test_name = str_text == _current_test.name - - if(!printing_test_name): - if(to_print != ""): - to_print += "\n" - #Make the indent - var pad = "" - for i in range(0, indent): - pad += " " - to_print += pad + str_text - to_print = to_print.replace("\n", "\n" + pad) - - if(_should_print_to_console): - print(to_print) - - _log_text += to_print + "\n" - - _gui.get_text_box().insert_text_at_cursor(to_print + "\n") - -################ -# -# RUN TESTS/ADD SCRIPTS -# -################ -func get_minimum_size(): - return Vector2(810, 380) - -# ------------------------------------------------------------------------------ -# Runs all the scripts that were added using add_script -# ------------------------------------------------------------------------------ -func test_scripts(run_rest=false): - clear_text() - - 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) - else: - _test_the_scripts(indexes) - else: - _test_the_scripts([]) - - -# ------------------------------------------------------------------------------ -# Runs a single script passed in. -# ------------------------------------------------------------------------------ -func test_script(script): - _test_collector.set_test_class_prefix(_inner_class_prefix) - _test_collector.clear() - _test_collector.add_script(script) - _test_the_scripts() - -# ------------------------------------------------------------------------------ -# Adds a script to be run when test_scripts called -# -# No longer supports selecting a script via this method. -# ------------------------------------------------------------------------------ -func add_script(script, was_select_this_one=null): - if(was_select_this_one != null): - _lgr.error('The option to select a script when using add_script has been removed. Calling add_script with 2 parameters will be removed in a later release.') - - if(!Engine.is_editor_hint()): - _test_collector.set_test_class_prefix(_inner_class_prefix) - _test_collector.add_script(script) - _add_scripts_to_gui() - -# ------------------------------------------------------------------------------ -# Add all scripts in the specified directory that start with the prefix and end -# with the suffix. Does not look in sub directories. Can be called multiple -# times. -# ------------------------------------------------------------------------------ -func add_directory(path, prefix=_file_prefix, suffix=_file_extension): - var d = Directory.new() - # 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. - if(path == '' or !d.dir_exists(path)): - if(path != ''): - _lgr.error(str('The path [', path, '] does not exist.')) - return - - var files = _get_files(path, prefix, suffix) - for i in range(files.size()): - add_script(files[i]) - -# ------------------------------------------------------------------------------ -# This will try to find a script in the list of scripts to test that contains -# the specified script name. It does not have to be a full match. It will -# select the first matching occurrence so that this script will run when run_tests -# is called. Works the same as the select_this_one option of add_script. -# -# returns whether it found a match or not -# ------------------------------------------------------------------------------ -func select_script(script_name): - _script_name = script_name - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func export_tests(path=_export_path): - if(path == null): - _lgr.error('You must pass a path or set the export_path before calling export_tests') - else: - var result = _test_collector.export_tests(path) - if(result): - p(_test_collector.to_s()) - p("Exported to " + path) - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func import_tests(path=_export_path): - if(!_utils.file_exists(path)): - _lgr.error(str('Cannot import tests: the path [', path, '] does not exist.')) - else: - _test_collector.clear() - var result = _test_collector.import_tests(path) - if(result): - p(_test_collector.to_s()) - p("Imported from " + path) - _add_scripts_to_gui() - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func import_tests_if_none_found(): - if(_test_collector.scripts.size() == 0): - import_tests() - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func export_if_tests_found(): - if(_test_collector.scripts.size() > 0): - export_tests() -################ -# -# MISC -# -################ - -# ------------------------------------------------------------------------------ -# Maximize test runner window to fit the viewport. -# ------------------------------------------------------------------------------ -func set_should_maximize(should): - _should_maximize = should - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_should_maximize(): - return _should_maximize - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func maximize(): - _gui.maximize() - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func disable_strict_datatype_checks(should): - _disable_strict_datatype_checks = should - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func is_strict_datatype_checks_disabled(): - return _disable_strict_datatype_checks - -# ------------------------------------------------------------------------------ -# Pauses the test and waits for you to press a confirmation button. Useful when -# you want to watch a test play out onscreen or inspect results. -# ------------------------------------------------------------------------------ -func end_yielded_test(): - _lgr.deprecated('end_yielded_test is no longer necessary, you can remove it.') - -# ------------------------------------------------------------------------------ -# Clears the text of the text box. This resets all counters. -# ------------------------------------------------------------------------------ -func clear_text(): - _gui.get_text_box().set_text("") - _gui.get_text_box().clear_colors() - update() - -# ------------------------------------------------------------------------------ -# Get the number of tests that were ran -# ------------------------------------------------------------------------------ -func get_test_count(): - return _new_summary.get_totals().tests - -# ------------------------------------------------------------------------------ -# Get the number of assertions that were made -# ------------------------------------------------------------------------------ -func get_assert_count(): - var t = _new_summary.get_totals() - return t.passing + t.failing - -# ------------------------------------------------------------------------------ -# Get the number of assertions that passed -# ------------------------------------------------------------------------------ -func get_pass_count(): - return _new_summary.get_totals().passing - -# ------------------------------------------------------------------------------ -# Get the number of assertions that failed -# ------------------------------------------------------------------------------ -func get_fail_count(): - return _new_summary.get_totals().failing - -# ------------------------------------------------------------------------------ -# Get the number of tests flagged as pending -# ------------------------------------------------------------------------------ -func get_pending_count(): - return _new_summary.get_totals().pending - -# ------------------------------------------------------------------------------ -# Set whether it should print to console or not. Default is yes. -# ------------------------------------------------------------------------------ -func set_should_print_to_console(should): - _should_print_to_console = should - -# ------------------------------------------------------------------------------ -# Get whether it is printing to the console -# ------------------------------------------------------------------------------ -func get_should_print_to_console(): - return _should_print_to_console - -# ------------------------------------------------------------------------------ -# Get the results of all tests ran as text. This string is the same as is -# displayed in the text box, and similar to what is printed to the console. -# ------------------------------------------------------------------------------ -func get_result_text(): - return _log_text - -# ------------------------------------------------------------------------------ -# Set the log level. Use one of the various LOG_LEVEL_* constants. -# ------------------------------------------------------------------------------ -func set_log_level(level): - _log_level = level - if(!Engine.is_editor_hint()): - _gui.set_log_level(level) - -# ------------------------------------------------------------------------------ -# Get the current log level. -# ------------------------------------------------------------------------------ -func get_log_level(): - return _log_level - -# ------------------------------------------------------------------------------ -# Call this method to make the test pause before teardown so that you can inspect -# anything that you have rendered to the screen. -# ------------------------------------------------------------------------------ -func pause_before_teardown(): - _pause_before_teardown = true; - -# ------------------------------------------------------------------------------ -# For batch processing purposes, you may want to ignore any calls to -# pause_before_teardown that you forgot to remove. -# ------------------------------------------------------------------------------ -func set_ignore_pause_before_teardown(should_ignore): - _ignore_pause_before_teardown = should_ignore - _gui.set_ignore_pause(should_ignore) - -func get_ignore_pause_before_teardown(): - return _ignore_pause_before_teardown - -# ------------------------------------------------------------------------------ -# Set to true so that painting of the screen will occur between tests. Allows you -# to see the output as tests occur. Especially useful with long running tests that -# make it appear as though it has humg. -# -# NOTE: not compatible with 1.0 so this is disabled by default. This will -# change in future releases. -# ------------------------------------------------------------------------------ -func set_yield_between_tests(should): - _yield_between.should = should - -func get_yield_between_tests(): - return _yield_between.should - -# ------------------------------------------------------------------------------ -# Call _process or _fixed_process, if they exist, on obj and all it's children -# and their children and so and so forth. Delta will be passed through to all -# the _process or _fixed_process methods. -# ------------------------------------------------------------------------------ -func simulate(obj, times, delta): - for i in range(times): - if(obj.has_method("_process")): - obj._process(delta) - if(obj.has_method("_physics_process")): - obj._physics_process(delta) - - for kid in obj.get_children(): - simulate(kid, 1, delta) - -# ------------------------------------------------------------------------------ -# Starts an internal timer with a timeout of the passed in time. A 'timeout' -# signal will be sent when the timer ends. Returns itself so that it can be -# used in a call to yield...cutting down on lines of code. -# -# Example, yield to the Gut object for 10 seconds: -# yield(gut.set_yield_time(10), 'timeout') -# ------------------------------------------------------------------------------ -func set_yield_time(time, text=''): - _yield_timer.set_wait_time(time) - _yield_timer.start() - var msg = '/# Yielding (' + str(time) + 's)' - if(text == ''): - msg += ' #/' - else: - msg += ': ' + text + ' #/' - p(msg, 1) - _was_yield_method_called = true - return self - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_yield_signal_or_time(obj, signal_name, max_wait, text=''): - obj.connect(signal_name, self, '_yielding_callback', [true]) - _yielding_to.obj = obj - _yielding_to.signal_name = signal_name - - _yield_timer.set_wait_time(max_wait) - _yield_timer.start() - _was_yield_method_called = true - p(str('/# Yielding to signal "', signal_name, '" or for ', max_wait, ' seconds #/')) - return self - -# ------------------------------------------------------------------------------ -# get the specific unit test that should be run -# ------------------------------------------------------------------------------ -func get_unit_test_name(): - return _unit_test_name - -# ------------------------------------------------------------------------------ -# set the specific unit test that should be run. -# ------------------------------------------------------------------------------ -func set_unit_test_name(test_name): - _unit_test_name = test_name - -# ------------------------------------------------------------------------------ -# Creates an empty file at the specified path -# ------------------------------------------------------------------------------ -func file_touch(path): - var f = File.new() - f.open(path, f.WRITE) - f.close() - -# ------------------------------------------------------------------------------ -# deletes the file at the specified path -# ------------------------------------------------------------------------------ -func file_delete(path): - var d = Directory.new() - var result = d.open(path.get_base_dir()) - if(result == OK): - d.remove(path) - -# ------------------------------------------------------------------------------ -# Checks to see if the passed in file has any data in it. -# ------------------------------------------------------------------------------ -func is_file_empty(path): - var f = File.new() - f.open(path, f.READ) - var empty = f.get_len() == 0 - f.close() - return empty - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_file_as_text(path): - var to_return = '' - var f = File.new() - f.open(path, f.READ) - to_return = f.get_as_text() - f.close() - return to_return -# ------------------------------------------------------------------------------ -# deletes all files in a given directory -# ------------------------------------------------------------------------------ -func directory_delete_files(path): - var d = Directory.new() - var result = d.open(path) - - # SHORTCIRCUIT - if(result != OK): - return - - # Traversing a directory is kinda odd. You have to start the process of listing - # the contents of a directory with list_dir_begin then use get_next until it - # returns an empty string. Then I guess you should end it. - d.list_dir_begin() - var thing = d.get_next() # could be a dir or a file or something else maybe? - var full_path = '' - while(thing != ''): - full_path = path + "/" + thing - #file_exists returns fasle for directories - if(d.file_exists(full_path)): - d.remove(full_path) - thing = d.get_next() - d.list_dir_end() - -# ------------------------------------------------------------------------------ -# Returns the instantiated script object that is currently being run. -# ------------------------------------------------------------------------------ -func get_current_script_object(): - var to_return = null - if(_test_script_objects.size() > 0): - to_return = _test_script_objects[-1] - return to_return - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_current_test_object(): - return _current_test - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_stubber(): - return _stubber - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_doubler(): - return _doubler - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_spy(): - return _spy - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_temp_directory(): - return _temp_directory - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_temp_directory(temp_directory): - _temp_directory = temp_directory - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_inner_class_name(): - return _inner_class_name - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_inner_class_name(inner_class_name): - _inner_class_name = inner_class_name - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_summary(): - return _new_summary - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_double_strategy(): - return _double_strategy - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_double_strategy(double_strategy): - _double_strategy = double_strategy - _doubler.set_strategy(double_strategy) - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_include_subdirectories(): - return _include_subdirectories - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_logger(): - return _lgr - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_logger(logger): - _lgr = logger - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_include_subdirectories(include_subdirectories): - _include_subdirectories = include_subdirectories - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_test_collector(): - return _test_collector - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_export_path(): - return _export_path - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_export_path(export_path): - _export_path = export_path - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_version(): - return _version - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_pre_run_script(): - return _pre_run_script - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_pre_run_script(pre_run_script): - _pre_run_script = pre_run_script - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_post_run_script(): - return _post_run_script - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func set_post_run_script(post_run_script): - _post_run_script = post_run_script - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_pre_run_script_instance(): - return _pre_run_script_instance - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func get_post_run_script_instance(): - return _post_run_script_instance diff --git a/addons/gut/gut_cmdln.gd b/addons/gut/gut_cmdln.gd deleted file mode 100644 index 555f3be..0000000 --- a/addons/gut/gut_cmdln.gd +++ /dev/null @@ -1,356 +0,0 @@ -################################################################################ -#(G)odot (U)nit (T)est class -# -################################################################################ -#The MIT License (MIT) -#===================== -# -#Copyright (c) 2019 Tom "Butch" Wesley -# -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: -# -#The above copyright notice and this permission notice shall be included in -#all copies or substantial portions of the Software. -# -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -#THE SOFTWARE. -# -################################################################################ -# Description -# ----------- -# Command line interface for the GUT unit testing tool. Allows you to run tests -# from the command line instead of running a scene. Place this script along with -# gut.gd into your scripts directory at the root of your project. Once there you -# can run this script (from the root of your project) using the following command: -# godot -s -d test/gut/gut_cmdln.gd -# -# See the readme for a list of options and examples. You can also use the -gh -# option to get more information about how to use the command line interface. -# -# Version 6.8.1 -################################################################################ -extends SceneTree - - -var Optparse = load('res://addons/gut/optparse.gd') -var Gut = load('res://addons/gut/gut.gd') - -#------------------------------------------------------------------------------- -# Helper class to resolve the various different places where an option can -# be set. Using the get_value method will enforce the order of precedence of: -# 1. command line value -# 2. config file value -# 3. default value -# -# The idea is that you set the base_opts. That will get you a copies of the -# hash with null values for the other types of values. Lower precedented hashes -# will punch through null values of higher precedented hashes. -#------------------------------------------------------------------------------- -class OptionResolver: - var base_opts = null - var cmd_opts = null - var config_opts = null - - - func get_value(key): - return _nvl(cmd_opts[key], _nvl(config_opts[key], base_opts[key])) - - func set_base_opts(opts): - base_opts = opts - cmd_opts = _null_copy(opts) - config_opts = _null_copy(opts) - - # creates a copy of a hash with all values null. - func _null_copy(h): - var new_hash = {} - for key in h: - new_hash[key] = null - return new_hash - - func _nvl(a, b): - if(a == null): - return b - else: - return a - func _string_it(h): - var to_return = '' - for key in h: - to_return += str('(',key, ':', _nvl(h[key], 'NULL'), ')') - return to_return - - func to_s(): - return str("base:\n", _string_it(base_opts), "\n", \ - "config:\n", _string_it(config_opts), "\n", \ - "cmd:\n", _string_it(cmd_opts), "\n", \ - "resolved:\n", _string_it(get_resolved_values())) - - func get_resolved_values(): - var to_return = {} - for key in base_opts: - to_return[key] = get_value(key) - return to_return - - func to_s_verbose(): - var to_return = '' - var resolved = get_resolved_values() - for key in base_opts: - to_return += str(key, "\n") - to_return += str(' default: ', _nvl(base_opts[key], 'NULL'), "\n") - to_return += str(' config: ', _nvl(config_opts[key], ' --'), "\n") - to_return += str(' cmd: ', _nvl(cmd_opts[key], ' --'), "\n") - to_return += str(' final: ', _nvl(resolved[key], 'NULL'), "\n") - - return to_return - -#------------------------------------------------------------------------------- -# Here starts the actual script that uses the Options class to kick off Gut -# and run your tests. -#------------------------------------------------------------------------------- -var _utils = load('res://addons/gut/utils.gd').new() -# instance of gut -var _tester = null -# array of command line options specified -var _opts = [] -# Hash for easier access to the options in the code. Options will be -# extracted into this hash and then the hash will be used afterwards so -# that I don't make any dumb typos and get the neat code-sense when I -# type a dot. -var options = { - config_file = 'res://.gutconfig.json', - dirs = [], - double_strategy = 'partial', - ignore_pause = false, - include_subdirs = false, - inner_class = '', - log_level = 1, - opacity = 100, - prefix = 'test_', - selected = '', - should_exit = false, - should_exit_on_success = false, - should_maximize = false, - show_help = false, - suffix = '.gd', - tests = [], - unit_test_name = '', - pre_run_script = '', - post_run_script = '' -} - -# flag to indicate if only a single script should be run. -var _run_single = false - -func setup_options(): - var opts = Optparse.new() - opts.set_banner(('This is the command line interface for the unit testing tool Gut. With this ' + - 'interface you can run one or more test scripts from the command line. In order ' + - 'for the Gut options to not clash with any other godot options, each option starts ' + - 'with a "g". Also, any option that requires a value will take the form of ' + - '"-g=". There cannot be any spaces between the option, the "=", or ' + - 'inside a specified value or godot will think you are trying to run a scene.')) - opts.add('-gtest', [], 'Comma delimited list of full paths to test scripts to run.') - opts.add('-gdir', [], 'Comma delimited list of directories to add tests from.') - opts.add('-gprefix', 'test_', 'Prefix used to find tests when specifying -gdir. Default "[default]"') - opts.add('-gsuffix', '.gd', 'Suffix used to find tests when specifying -gdir. Default "[default]"') - opts.add('-gmaximize', false, 'Maximizes test runner window to fit the viewport.') - opts.add('-gexit', false, 'Exit after running tests. If not specified you have to manually close the window.') - opts.add('-gexit_on_success', false, 'Only exit if all tests pass.') - opts.add('-glog', 1, 'Log level. Default [default]') - opts.add('-gignore_pause', false, 'Ignores any calls to gut.pause_before_teardown.') - opts.add('-gselect', '', ('Select a script to run initially. The first script that ' + - 'was loaded using -gtest or -gdir that contains the specified ' + - 'string will be executed. You may run others by interacting ' + - 'with the GUI.')) - opts.add('-gunit_test_name', '', ('Name of a test to run. Any test that contains the specified ' + - 'text will be run, all others will be skipped.')) - opts.add('-gh', false, 'Print this help, then quit') - opts.add('-gconfig', 'res://.gutconfig.json', 'A config file that contains configuration information. Default is res://.gutconfig.json') - opts.add('-ginner_class', '', 'Only run inner classes that contain this string') - opts.add('-gopacity', 100, 'Set opacity of test runner window. Use range 0 - 100. 0 = transparent, 100 = opaque.') - opts.add('-gpo', false, 'Print option values from all sources and the value used, then quit.') - opts.add('-ginclude_subdirs', false, 'Include subdirectories of -gdir.') - opts.add('-gdouble_strategy', 'partial', 'Default strategy to use when doubling. Valid values are [partial, full]. Default "[default]"') - opts.add('-gpre_run_script', '', 'pre-run hook script path') - opts.add('-gpost_run_script', '', 'post-run hook script path') - opts.add('-gprint_gutconfig_sample', false, 'Print out json that can be used to make a gutconfig file then quit.') - return opts - - -# Parses options, applying them to the _tester or setting values -# in the options struct. -func extract_command_line_options(from, to): - to.tests = from.get_value('-gtest') - to.dirs = from.get_value('-gdir') - to.should_exit = from.get_value('-gexit') - to.should_exit_on_success = from.get_value('-gexit_on_success') - to.should_maximize = from.get_value('-gmaximize') - to.log_level = from.get_value('-glog') - to.ignore_pause = from.get_value('-gignore_pause') - to.selected = from.get_value('-gselect') - to.prefix = from.get_value('-gprefix') - to.suffix = from.get_value('-gsuffix') - to.unit_test_name = from.get_value('-gunit_test_name') - to.config_file = from.get_value('-gconfig') - to.inner_class = from.get_value('-ginner_class') - to.opacity = from.get_value('-gopacity') - to.include_subdirs = from.get_value('-ginclude_subdirs') - to.double_strategy = from.get_value('-gdouble_strategy') - to.pre_run_script = from.get_value('-gpre_run_script') - to.post_run_script = from.get_value('-gpost_run_script') - - -func load_options_from_config_file(file_path, into): - # SHORTCIRCUIT - var f = File.new() - if(!f.file_exists(file_path)): - if(file_path != 'res://.gutconfig.json'): - print('ERROR: Config File "', file_path, '" does not exist.') - return -1 - else: - return 1 - - f.open(file_path, f.READ) - var json = f.get_as_text() - f.close() - - var results = JSON.parse(json) - # SHORTCIRCUIT - if(results.error != OK): - print("\n\n",'!! ERROR parsing file: ', file_path) - print(' at line ', results.error_line, ':') - print(' ', results.error_string) - return -1 - - # 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)): - into[key] = results.result[key] - - return 1 - -# Apply all the options specified to _tester. This is where the rubber meets -# the road. -func apply_options(opts): - _tester = Gut.new() - get_root().add_child(_tester) - _tester.connect('tests_finished', self, '_on_tests_finished', [opts.should_exit, opts.should_exit_on_success]) - _tester.set_yield_between_tests(true) - _tester.set_modulate(Color(1.0, 1.0, 1.0, min(1.0, float(opts.opacity) / 100))) - _tester.show() - - _tester.set_include_subdirectories(opts.include_subdirs) - - if(opts.should_maximize): - _tester.maximize() - - if(opts.inner_class != ''): - _tester.set_inner_class_name(opts.inner_class) - _tester.set_log_level(opts.log_level) - _tester.set_ignore_pause_before_teardown(opts.ignore_pause) - - 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(_utils.DOUBLE_STRATEGY.FULL) - elif(opts.double_strategy == 'partial'): - _tester.set_double_strategy(_utils.DOUBLE_STRATEGY.PARTIAL) - - _tester.set_unit_test_name(opts.unit_test_name) - _tester.set_pre_run_script(opts.pre_run_script) - _tester.set_post_run_script(opts.post_run_script) - -func _print_gutconfigs(values): - var header = """Here is a sample of a full .gutconfig.json file. -You do not need to specify all values in your own file. The values supplied in -this sample are what would be used if you ran gut w/o the -gprint_gutconfig_sample -option (the resolved values where default < .gutconfig < command line).""" - print("\n", header.replace("\n", ' '), "\n\n") - var resolved = values - - # remove some options that don't make sense to be in config - resolved.erase("config_file") - resolved.erase("show_help") - - print("Here's a config with all the properties set based off of your current command and config.") - var text = JSON.print(resolved) - print(text.replace(',', ",\n")) - - for key in resolved: - resolved[key] = null - - print("\n\nAnd here's an empty config for you fill in what you want.") - text = JSON.print(resolved) - print(text.replace(',', ",\n")) - - -# parse options and run Gut -func _init(): - var opt_resolver = OptionResolver.new() - opt_resolver.set_base_opts(options) - - print("\n\n", ' --- Gut ---') - var o = setup_options() - - var all_options_valid = o.parse() - extract_command_line_options(o, opt_resolver.cmd_opts) - var load_result = \ - load_options_from_config_file(opt_resolver.get_value('config_file'), opt_resolver.config_opts) - - if(load_result == -1): # -1 indicates json parse error - quit() - else: - if(!all_options_valid): - quit() - elif(o.get_value('-gh')): - var v_info = Engine.get_version_info() - print(str('Godot version: ', v_info.major, '.', v_info.minor, '.', v_info.patch)) - print(str('GUT version: ', Gut.new().get_version())) - - o.print_help() - quit() - elif(o.get_value('-gpo')): - print('All command line options and where they are specified. ' + - 'The "final" value shows which value will actually be used ' + - 'based on order of precedence (default < .gutconfig < cmd line).' + "\n") - print(opt_resolver.to_s_verbose()) - quit() - elif(o.get_value('-gprint_gutconfig_sample')): - _print_gutconfigs(opt_resolver.get_resolved_values()) - quit() - else: - apply_options(opt_resolver.get_resolved_values()) - _tester.test_scripts(!_run_single) - -# exit if option is set. -func _on_tests_finished(should_exit, should_exit_on_success): - if(_tester.get_fail_count()): - OS.exit_code = 1 - - # Overwrite the exit code with the post_script - var post_inst = _tester.get_post_run_script_instance() - if(post_inst != null and post_inst.get_exit_code() != null): - OS.exit_code = post_inst.get_exit_code() - - if(should_exit or (should_exit_on_success and _tester.get_fail_count() == 0)): - quit() - else: - print("Tests finished, exit manually") diff --git a/addons/gut/gut_plugin.gd b/addons/gut/gut_plugin.gd deleted file mode 100644 index b7c24f1..0000000 --- a/addons/gut/gut_plugin.gd +++ /dev/null @@ -1,12 +0,0 @@ -tool -extends EditorPlugin - -func _enter_tree(): - # Initialization of the plugin goes here - # Add the new type with a name, a parent type, a script and an icon - add_custom_type("Gut", "Control", preload("gut.gd"), preload("icon.png")) - -func _exit_tree(): - # Clean-up of the plugin goes here - # Always remember to remove it from the engine when deactivated - remove_custom_type("Gut") diff --git a/addons/gut/hook_script.gd b/addons/gut/hook_script.gd deleted file mode 100644 index 10a5e12..0000000 --- a/addons/gut/hook_script.gd +++ /dev/null @@ -1,35 +0,0 @@ -# ------------------------------------------------------------------------------ -# This script is the base for custom scripts to be used in pre and post -# run hooks. -# ------------------------------------------------------------------------------ - -# This is the instance of GUT that is running the tests. You can get -# information about the run from this object. This is set by GUT when the -# script is instantiated. -var gut = null - -# the exit code to be used by gut_cmdln. See set method. -var _exit_code = null - -var _should_abort = false -# Virtual method that will be called by GUT after instantiating -# this script. -func run(): - pass - -# Set the exit code when running from the command line. If not set then the -# default exit code will be returned (0 when no tests fail, 1 when any tests -# fail). -func set_exit_code(code): - _exit_code = code - -func get_exit_code(): - return _exit_code - -# Usable by pre-run script to cause the run to end AFTER the run() method -# finishes. post-run script will not be ran. -func abort(): - _should_abort = true - -func should_abort(): - return _should_abort diff --git a/addons/gut/icon.png b/addons/gut/icon.png deleted file mode 100644 index 7c589874e72ccdeae8dc1a45907fba0d63e603cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8Uop6XFV_m%d*!21sKVXw&;xMaGo1Os_Qt@2OC7#SFv=^B{n8kvR|7+D!uSecq=gBS*JG9l+sH00)|WTsW()}ZhBrU+z( Mr>mdKI;Vst04A4Hj{pDw diff --git a/addons/gut/icon.png.import b/addons/gut/icon.png.import deleted file mode 100644 index 848473e..0000000 --- a/addons/gut/icon.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.stex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/gut/icon.png" -dest_files=[ "res://.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.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 -stream=false -size_limit=0 -detect_3d=true -svg/scale=1.0 diff --git a/addons/gut/logger.gd b/addons/gut/logger.gd deleted file mode 100644 index c7cd458..0000000 --- a/addons/gut/logger.gd +++ /dev/null @@ -1,105 +0,0 @@ -extends Node2D - -var _gut = null - -var types = { - warn = 'WARNING', - error = 'ERROR', - info = 'INFO', - debug = 'DEBUG', - deprecated = 'DEPRECATED' -} - -var _logs = { - types.warn: [], - types.error: [], - types.info: [], - types.debug: [], - types.deprecated: [] -} - -var _suppress_output = false - -func _gut_log_level_for_type(log_type): - if(log_type == types.warn or log_type == types.error or log_type == types.deprecated): - return 0 - else: - return 2 - -func _log(type, text): - _logs[type].append(text) - var formatted = str('[', type, '] ', text) - if(!_suppress_output): - if(_gut): - # this will keep the text indented under test for readability - _gut.p(formatted, _gut_log_level_for_type(type)) - # IDEA! We could store the current script and test that generated - # this output, which could be useful later if we printed out a summary. - else: - print(formatted) - return formatted - -# --------------- -# Get Methods -# --------------- -func get_warnings(): - return get_log_entries(types.warn) - -func get_errors(): - return get_log_entries(types.error) - -func get_infos(): - return get_log_entries(types.info) - -func get_debugs(): - return get_log_entries(types.debug) - -func get_deprecated(): - return get_log_entries(types.deprecated) - -func get_count(log_type=null): - var count = 0 - if(log_type == null): - for key in _logs: - count += _logs[key].size() - else: - count = _logs[log_type].size() - return count - -func get_log_entries(log_type): - return _logs[log_type] - -# --------------- -# Log methods -# --------------- -func warn(text): - return _log(types.warn, text) - -func error(text): - return _log(types.error, text) - -func info(text): - return _log(types.info, text) - -func debug(text): - return _log(types.debug, text) - -# supply some text or the name of the deprecated method and the replacement. -func deprecated(text, alt_method=null): - var msg = text - if(alt_method): - msg = str('The method ', text, ' is deprecated, use ', alt_method , ' instead.') - return _log(types.deprecated, msg) - -# --------------- -# Misc -# --------------- -func get_gut(): - return _gut - -func set_gut(gut): - _gut = gut - -func clear(): - for key in _logs: - _logs[key].clear() diff --git a/addons/gut/method_maker.gd b/addons/gut/method_maker.gd deleted file mode 100644 index 632511d..0000000 --- a/addons/gut/method_maker.gd +++ /dev/null @@ -1,200 +0,0 @@ -# This class will generate method declaration lines based on method meta -# data. It will create defaults that match the method data. -# -# -------------------- -# function meta data -# -------------------- -# name: -# flags: -# args: [{ -# (class_name:), -# (hint:0), -# (hint_string:), -# (name:), -# (type:4), -# (usage:7) -# }] -# default_args [] - -var _utils = load('res://addons/gut/utils.gd').new() -var _lgr = _utils.get_logger() -const PARAM_PREFIX = 'p_' - -# ------------------------------------------------------ -# _supported_defaults -# -# This array contains all the data types that are supported for default values. -# If a value is supported it will contain either an empty string or a prefix -# that should be used when setting the parameter default value. -# For example int, real, bool do not need anything func(p1=1, p2=2.2, p3=false) -# but things like Vectors and Colors do since only the parameters to create a -# new Vector or Color are included in the metadata. -# ------------------------------------------------------ - # TYPE_NIL = 0 — Variable is of type nil (only applied for null). - # TYPE_BOOL = 1 — Variable is of type bool. - # TYPE_INT = 2 — Variable is of type int. - # TYPE_REAL = 3 — Variable is of type float/real. - # TYPE_STRING = 4 — Variable is of type String. - # TYPE_VECTOR2 = 5 — Variable is of type Vector2. - # TYPE_RECT2 = 6 — Variable is of type Rect2. - # TYPE_VECTOR3 = 7 — Variable is of type Vector3. - # TYPE_COLOR = 14 — Variable is of type Color. - # TYPE_OBJECT = 17 — Variable is of type Object. - # TYPE_DICTIONARY = 18 — Variable is of type Dictionary. - # TYPE_ARRAY = 19 — Variable is of type Array. - # TYPE_VECTOR2_ARRAY = 24 — Variable is of type PoolVector2Array. - - - -# TYPE_TRANSFORM2D = 8 — Variable is of type Transform2D. -# TYPE_PLANE = 9 — Variable is of type Plane. -# TYPE_QUAT = 10 — Variable is of type Quat. -# TYPE_AABB = 11 — Variable is of type AABB. -# TYPE_BASIS = 12 — Variable is of type Basis. -# TYPE_TRANSFORM = 13 — Variable is of type Transform. -# TYPE_NODE_PATH = 15 — Variable is of type NodePath. -# TYPE_RID = 16 — Variable is of type RID. -# TYPE_RAW_ARRAY = 20 — Variable is of type PoolByteArray. -# 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_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. -# ------------------------------------------------------ -var _supported_defaults = [] - -func _init(): - for i in range(TYPE_MAX): - _supported_defaults.append(null) - - # These types do not require a prefix for defaults - _supported_defaults[TYPE_NIL] = '' - _supported_defaults[TYPE_BOOL] = '' - _supported_defaults[TYPE_INT] = '' - _supported_defaults[TYPE_REAL] = '' - _supported_defaults[TYPE_OBJECT] = '' - _supported_defaults[TYPE_ARRAY] = '' - _supported_defaults[TYPE_STRING] = '' - _supported_defaults[TYPE_DICTIONARY] = '' - _supported_defaults[TYPE_VECTOR2_ARRAY] = '' - - # These require a prefix for whatever default is provided - _supported_defaults[TYPE_VECTOR2] = 'Vector2' - _supported_defaults[TYPE_RECT2] = 'Rect2' - _supported_defaults[TYPE_VECTOR3] = 'Vector3' - _supported_defaults[TYPE_COLOR] = 'Color' - -# ############### -# Private -# ############### - -func _is_supported_default(type_flag): - return type_flag >= 0 and type_flag < _supported_defaults.size() and [type_flag] != null - -# Creates a list of parameters with defaults of null unless a default value is -# found in the metadata. If a default is found in the meta then it is used if -# it is one we know how support. -# -# If a default is found that we don't know how to handle then this method will -# return null. -func _get_arg_text(method_meta): - var text = '' - var args = method_meta.args - var defaults = [] - var has_unsupported_defaults = false - - # fill up the defaults with null defaults for everything that doesn't have - # a default in the meta data. default_args is an array of default values - # for the last n parameters where n is the size of default_args so we only - # add nulls for everything up to the first parameter with a default. - for i in range(args.size() - method_meta.default_args.size()): - defaults.append('null') - - # Add meta-data defaults. - for i in range(method_meta.default_args.size()): - var t = args[defaults.size()]['type'] - var value = '' - if(_is_supported_default(t)): - # strings are special, they need quotes around the value - if(t == TYPE_STRING): - value = str("'", str(method_meta.default_args[i]), "'") - # Colors need the parens but things like Vector2 and Rect2 don't - elif(t == TYPE_COLOR): - value = str(_supported_defaults[t], '(', str(method_meta.default_args[i]), ')') - elif(t == TYPE_OBJECT): - if(str(method_meta.default_args[i]) == "[Object:null]"): - value = str(_supported_defaults[t], 'null') - else: - value = str(_supported_defaults[t], str(method_meta.default_args[i]).to_lower()) - - # Everything else puts the prefix (if one is there) form _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: - value = str(_supported_defaults[t], str(method_meta.default_args[i]).to_lower()) - else: - _lgr.warn(str( - 'Unsupported default param type: ',method_meta.name, '-', args[defaults.size()].name, ' ', t, ' = ', method_meta.default_args[i])) - value = str('unsupported=',t) - has_unsupported_defaults = true - - defaults.append(value) - - # construct the string of parameters - for i in range(args.size()): - text += str(PARAM_PREFIX, args[i].name, '=', defaults[i]) - if(i != args.size() -1): - text += ', ' - - # if we don't know how to make a default then we have to return null b/c - # it will cause a runtime error and it's one thing we could return to let - # callers know it didn't work. - if(has_unsupported_defaults): - text = null - - return text - -# ############### -# Public -# ############### - -# 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. -func get_decleration_text(meta): - var param_text = _get_arg_text(meta) - var text = null - if(param_text != null): - text = str('func ', meta.name, '(', param_text, '):') - return text - -# creates a call to the function in meta in the super's class. -func get_super_call_text(meta): - var params = '' - var all_supported = true - - for i in range(meta.args.size()): - params += PARAM_PREFIX + meta.args[i].name - if(meta.args.size() > 1 and i != meta.args.size() -1): - params += ', ' - - return str('.', meta.name, '(', params, ')') - -func get_spy_call_parameters_text(meta): - var called_with = 'null' - if(meta.args.size() > 0): - called_with = '[' - for i in range(meta.args.size()): - called_with += str(PARAM_PREFIX, meta.args[i].name) - if(i < meta.args.size() - 1): - called_with += ', ' - called_with += ']' - return called_with - -func get_logger(): - return _lgr - -func set_logger(logger): - _lgr = logger diff --git a/addons/gut/one_to_many.gd b/addons/gut/one_to_many.gd deleted file mode 100644 index 6a0f818..0000000 --- a/addons/gut/one_to_many.gd +++ /dev/null @@ -1,38 +0,0 @@ -# ------------------------------------------------------------------------------ -# This datastructure represents a simple one-to-many relationship. It manages -# a dictionary of value/array pairs. It ignores duplicates of both the "one" -# and the "many". -# ------------------------------------------------------------------------------ -var _items = {} - -# return the size of _items or the size of an element in _items if "one" was -# specified. -func size(one=null): - var to_return = 0 - if(one == null): - to_return = _items.size() - elif(_items.has(one)): - to_return = _items[one].size() - return to_return - -# Add an element to "one" if it does not already exist -func add(one, many_item): - if(_items.has(one) and !_items[one].has(many_item)): - _items[one].append(many_item) - else: - _items[one] = [many_item] - -func clear(): - _items.clear() - -func has(one, many_item): - var to_return = false - if(_items.has(one)): - to_return = _items[one].has(many_item) - return to_return - -func to_s(): - var to_return = '' - for key in _items: - to_return += str(key, ": ", _items[key], "\n") - return to_return diff --git a/addons/gut/optparse.gd b/addons/gut/optparse.gd deleted file mode 100644 index ba21a65..0000000 --- a/addons/gut/optparse.gd +++ /dev/null @@ -1,250 +0,0 @@ -################################################################################ -#(G)odot (U)nit (T)est class -# -################################################################################ -#The MIT License (MIT) -#===================== -# -#Copyright (c) 2019 Tom "Butch" Wesley -# -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: -# -#The above copyright notice and this permission notice shall be included in -#all copies or substantial portions of the Software. -# -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -#THE SOFTWARE. -# -################################################################################ -# Description -# ----------- -# Command line interface for the GUT unit testing tool. Allows you to run tests -# from the command line instead of running a scene. Place this script along with -# gut.gd into your scripts directory at the root of your project. Once there you -# can run this script (from the root of your project) using the following command: -# godot -s -d test/gut/gut_cmdln.gd -# -# See the readme for a list of options and examples. You can also use the -gh -# option to get more information about how to use the command line interface. -# -# Version 6.8.1 -################################################################################ - -#------------------------------------------------------------------------------- -# Parses the command line arguments supplied into an array that can then be -# examined and parsed based on how the gut options work. -#------------------------------------------------------------------------------- -class CmdLineParser: - var _used_options = [] - # an array of arrays. Each element in this array will contain an option - # name and if that option contains a value then it will have a sedond - # element. For example: - # [[-gselect, test.gd], [-gexit]] - var _opts = [] - - func _init(): - for i in range(OS.get_cmdline_args().size()): - var opt_val = OS.get_cmdline_args()[i].split('=') - _opts.append(opt_val) - - # Parse out multiple comma delimited values from a command line - # option. Values are separated from option name with "=" and - # additional values are comma separated. - func _parse_array_value(full_option): - var value = _parse_option_value(full_option) - var split = value.split(',') - return split - - # Parse out the value of an option. Values are separated from - # the option name with "=" - func _parse_option_value(full_option): - if(full_option.size() > 1): - return full_option[1] - else: - return null - - # Search _opts for an element that starts with the option name - # specified. - func find_option(name): - var found = false - var idx = 0 - - while(idx < _opts.size() and !found): - if(_opts[idx][0] == name): - found = true - else: - idx += 1 - - if(found): - return idx - else: - return -1 - - func get_array_value(option): - _used_options.append(option) - var to_return = [] - var opt_loc = find_option(option) - if(opt_loc != -1): - to_return = _parse_array_value(_opts[opt_loc]) - _opts.remove(opt_loc) - - return to_return - - # returns the value of an option if it was specified, null otherwise. This - # used to return the default but that became problemnatic when trying to - # punch through the different places where values could be specified. - func get_value(option): - _used_options.append(option) - var to_return = null - var opt_loc = find_option(option) - if(opt_loc != -1): - to_return = _parse_option_value(_opts[opt_loc]) - _opts.remove(opt_loc) - - return to_return - - # returns true if it finds the option, false if not. - func was_specified(option): - _used_options.append(option) - return find_option(option) != -1 - - # Returns any unused command line options. I found that only the -s and - # script name come through from godot, all other options that godot uses - # are not sent through OS.get_cmdline_args(). - # - # This is a onetime thing b/c i kill all items in _used_options - func get_unused_options(): - var to_return = [] - for i in range(_opts.size()): - to_return.append(_opts[i][0]) - - var script_option = to_return.find('-s') - if script_option != -1: - to_return.remove(script_option + 1) - to_return.remove(script_option) - - while(_used_options.size() > 0): - var index = to_return.find(_used_options[0].split("=")[0]) - if(index != -1): - to_return.remove(index) - _used_options.remove(0) - - return to_return - -#------------------------------------------------------------------------------- -# Simple class to hold a command line option -#------------------------------------------------------------------------------- -class Option: - var value = null - var option_name = '' - var default = null - var description = '' - - func _init(name, default_value, desc=''): - option_name = name - default = default_value - description = desc - value = null#default_value - - func pad(value, size, pad_with=' '): - var to_return = value - for i in range(value.length(), size): - to_return += pad_with - - return to_return - - func to_s(min_space=0): - var subbed_desc = description - if(subbed_desc.find('[default]') != -1): - subbed_desc = subbed_desc.replace('[default]', str(default)) - return pad(option_name, min_space) + subbed_desc - -#------------------------------------------------------------------------------- -# The high level interface between this script and the command line options -# supplied. Uses Option class and CmdLineParser to extract information from -# the command line and make it easily accessible. -#------------------------------------------------------------------------------- -var options = [] -var _opts = [] -var _banner = '' - -func add(name, default, desc): - options.append(Option.new(name, default, desc)) - -func get_value(name): - var found = false - var idx = 0 - - while(idx < options.size() and !found): - if(options[idx].option_name == name): - found = true - else: - idx += 1 - - if(found): - return options[idx].value - else: - print("COULD NOT FIND OPTION " + name) - return null - -func set_banner(banner): - _banner = banner - -func print_help(): - var longest = 0 - for i in range(options.size()): - if(options[i].option_name.length() > longest): - longest = options[i].option_name.length() - - print('---------------------------------------------------------') - print(_banner) - - print("\nOptions\n-------") - for i in range(options.size()): - print(' ' + options[i].to_s(longest + 2)) - print('---------------------------------------------------------') - -func print_options(): - for i in range(options.size()): - print(options[i].option_name + '=' + str(options[i].value)) - -func parse(): - var parser = CmdLineParser.new() - - for i in range(options.size()): - var t = typeof(options[i].default) - # only set values that were specified at the command line so that - # we can punch through default and config values correctly later. - # Without this check, you can't tell the difference between the - # defaults and what was specified, so you can't punch through - # higher level options. - if(parser.was_specified(options[i].option_name)): - if(t == TYPE_INT): - options[i].value = int(parser.get_value(options[i].option_name)) - elif(t == TYPE_STRING): - options[i].value = parser.get_value(options[i].option_name) - elif(t == TYPE_ARRAY): - options[i].value = parser.get_array_value(options[i].option_name) - elif(t == TYPE_BOOL): - options[i].value = parser.was_specified(options[i].option_name) - elif(t == TYPE_NIL): - print(options[i].option_name + ' cannot be processed, it has a nil datatype') - else: - print(options[i].option_name + ' cannot be processed, it has unknown datatype:' + str(t)) - - var unused = parser.get_unused_options() - if(unused.size() > 0): - print("Unrecognized options: ", unused) - return false - - return true diff --git a/addons/gut/plugin.cfg b/addons/gut/plugin.cfg deleted file mode 100644 index 0534270..0000000 --- a/addons/gut/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Gut" -description="Unit Testing tool for Godot." -author="Butch Wesley" -version="6.8.1" -script="gut_plugin.gd" diff --git a/addons/gut/signal_watcher.gd b/addons/gut/signal_watcher.gd deleted file mode 100644 index 1ee2180..0000000 --- a/addons/gut/signal_watcher.gd +++ /dev/null @@ -1,166 +0,0 @@ -################################################################################ -#The MIT License (MIT) -#===================== -# -#Copyright (c) 2019 Tom "Butch" Wesley -# -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: -# -#The above copyright notice and this permission notice shall be included in -#all copies or substantial portions of the Software. -# -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -#THE SOFTWARE. -# -################################################################################ - -# Some arbitrary string that should never show up by accident. If it does, then -# shame on you. -const ARG_NOT_SET = '_*_argument_*_is_*_not_set_*_' - -# This hash holds the objects that are being watched, the signals that are being -# watched, and an array of arrays that contains arguments that were passed -# each time the signal was emitted. -# -# For example: -# _watched_signals => { -# ref1 => { -# 'signal1' => [[], [], []], -# 'signal2' => [[p1, p2]], -# 'signal3' => [[p1]] -# }, -# ref2 => { -# 'some_signal' => [], -# 'other_signal' => [[p1, p2, p3], [p1, p2, p3], [p1, p2, p3]] -# } -# } -# -# In this sample: -# - signal1 on the ref1 object was emitted 3 times and each time, zero -# parameters were passed. -# - signal3 on ref1 was emitted once and passed a single parameter -# - some_signal on ref2 was never emitted. -# - other_signal on ref2 was emitted 3 times, each time with 3 parameters. -var _watched_signals = {} -var _utils = load('res://addons/gut/utils.gd').new() - -func _add_watched_signal(obj, name): - # SHORTCIRCUIT - ignore dupes - if(_watched_signals.has(obj) and _watched_signals[obj].has(name)): - return - - if(!_watched_signals.has(obj)): - _watched_signals[obj] = {name:[]} - else: - _watched_signals[obj][name] = [] - obj.connect(name, self, '_on_watched_signal', [obj, name]) - -# This handles all the signals that are watched. It supports up to 9 parameters -# which could be emitted by the signal and the two parameters used when it is -# connected via watch_signal. I chose 9 since you can only specify up to 9 -# parameters when dynamically calling a method via call (per the Godot -# documentation, i.e. some_object.call('some_method', 1, 2, 3...)). -# -# Based on the documentation of emit_signal, it appears you can only pass up -# to 4 parameters when firing a signal. I haven't verified this, but this should -# future proof this some if the value ever grows. -func _on_watched_signal(arg1=ARG_NOT_SET, arg2=ARG_NOT_SET, arg3=ARG_NOT_SET, \ - arg4=ARG_NOT_SET, arg5=ARG_NOT_SET, arg6=ARG_NOT_SET, \ - arg7=ARG_NOT_SET, arg8=ARG_NOT_SET, arg9=ARG_NOT_SET, \ - arg10=ARG_NOT_SET, arg11=ARG_NOT_SET): - var args = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11] - - # strip off any unused vars. - var idx = args.size() -1 - while(str(args[idx]) == ARG_NOT_SET): - args.remove(idx) - idx -= 1 - - # retrieve object and signal name from the array and remove them. These - # will always be at the end since they are added when the connect happens. - var signal_name = args[args.size() -1] - args.pop_back() - var object = args[args.size() -1] - args.pop_back() - - _watched_signals[object][signal_name].append(args) - -func does_object_have_signal(object, signal_name): - var signals = object.get_signal_list() - for i in range(signals.size()): - if(signals[i]['name'] == signal_name): - return true - return false - -func watch_signals(object): - var signals = object.get_signal_list() - for i in range(signals.size()): - _add_watched_signal(object, signals[i]['name']) - -func watch_signal(object, signal_name): - var did = false - if(does_object_have_signal(object, signal_name)): - _add_watched_signal(object, signal_name) - did = true - return did - -func get_emit_count(object, signal_name): - var to_return = -1 - if(is_watching(object, signal_name)): - to_return = _watched_signals[object][signal_name].size() - return to_return - -func did_emit(object, signal_name): - var did = false - if(is_watching(object, signal_name)): - did = get_emit_count(object, signal_name) != 0 - return did - -func print_object_signals(object): - var list = object.get_signal_list() - for i in range(list.size()): - print(list[i].name, "\n ", list[i]) - -func get_signal_parameters(object, signal_name, index=-1): - var params = null - if(is_watching(object, signal_name)): - var all_params = _watched_signals[object][signal_name] - if(all_params.size() > 0): - if(index == -1): - index = all_params.size() -1 - params = all_params[index] - return params - -func is_watching_object(object): - return _watched_signals.has(object) - -func is_watching(object, signal_name): - return _watched_signals.has(object) and _watched_signals[object].has(signal_name) - -func clear(): - for obj in _watched_signals: - for signal_name in _watched_signals[obj]: - if(_utils.is_not_freed(obj)): - obj.disconnect(signal_name, self, '_on_watched_signal') - _watched_signals.clear() - -# Returns a list of all the signal names that were emitted by the object. -# If the object is not being watched then an empty list is returned. -func get_signals_emitted(obj): - var emitted = [] - if(is_watching_object(obj)): - for signal_name in _watched_signals[obj]: - if(_watched_signals[obj][signal_name].size() > 0): - emitted.append(signal_name) - - return emitted diff --git a/addons/gut/source_code_pro.fnt b/addons/gut/source_code_pro.fnt deleted file mode 100644 index 3367650f77041f8020c39f0b1bb90d21e2ddf425..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26499 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

F`ud^Dyusp94YMD$s2-p z@AGePl{2Af;lGi@utuo(!w?+HezHf4Ep)h)`<1SP7l+1DQnyl^{i^USKo4`f|!kPZB3apzUVy%*qcWvvmnTw9Z;p} zUOPVf#^d0uv@45d1M#jzl0ZuooCn!|^AU>N1;>^%CU30KX3dUzXl4zjsR zX0%j)E%v&(Zt05@NNXa>-kzs_UDd!l(8tn{x7FkYfwL$3FQVW&4$ZvDpD_*S_5W%_+XrCqlWtKCbXFdR z)IP(m2D=rHv%smBcr3q>QL_kz1GQzw9Z|-Yiyoto5pX?J{Yz2%ne##Hj4#e3p$S9?s9K& z%?%S~HzToWe11iHXOKvZpS2L65ncyB?OK(&V1Q4SLN?~*%u5DTh3nMl$pz14{R57~ zh4;;rVB+Y-^_2HJAN=J6;~q89^(!53g{CE+PsrI~Tsy?qdl%hn!9O1xfrmUpkUAHk z(uOeu1n~+^TXW0>Ro5k|?;qiRnd3#V9u%#glK{XG=aR^mrR7!XPE{VfKd+#|yVuWR z4Sw3hn9r(wU?wCuRqVXLM3->{(1LFy=9t3)sZ)mGkF3t-hbn5H*{^7@G z0-@~yiIdRhz7=w%@7jMGtUXX3A=DG(d(vG)82!#jfuD&{O*lPbiCfYMSduDj zt0xH~msbNA*P9)ZqbjYSz1ILuB`H3odi(|^_n$dq z$mqVlNZaHH;q!XUa@zGy;ms0}4z9U0j>mhr-$r~-9fJpkr$z9PqE#YRd^|0jphM4R zEpdG;X*gFxq>Ttc1bJ_iJufrr;`EPoCpK2CN??ZwgXLHNB44QV8sQ5ZRN+LiwZ zttqKy|21NlE~gp4P3t`5g|W}=NCxp!kxuniT&ZOXF_{d2`)m!v6&Alb5izpt&_k40 zRa^xw+t6W6v! zXpEp^OZezDG{`2)H-6TM=JsL!?WA4GgVa9C&NsQ_NaaR0pB06dlFzz>{hEGW;{DIJ z8efG|2^%TZ=9Kf9x8I>Q{lPz9`@i4%Vw2%fUBW-Ik1ujtU*b0;^+C>ET_k97M2>A- z=-d{o8>M)ht~3a1lzh^w&uRK5N7?^G}$xNgq5+ z@cv9;7*eA#mts@rt{x=S?6^PdVX;qs15%}>ynwefuK^|Xm`l~x7Pd48u4V_^zj^cg zQYrobxJy?&CWBHlzG!j;7u%j#vuc(ch7rTiR0kJ`VVdiT_mobgAAXyCY#`2O+xqdj zm&~6H)v7nkMFbVS|Mh}0uG>7>!Ci6|%36bRz^w%1Z|i&oY@yYQcmk;u6ih<{MK>X z>cdBQ1|Hd=-aB(xyV!ty)>SL;oBIZ8n?GwyLC~qgYSBKyG?`Fl-vSKWL31j3u8Pt= z3LBY045pr9>{KA`n@g&Uk$mIw`f5pOWgqN-a=`Sg3pt;blk{AsM zItl{*;pqcTxU*OjX>cUpB4q6Dou7?w40}ldMBHE1atu|jCx%6hThru`-sS!gvrBoUkV=gm*3wvqePsjIQ%!TUFlufurq?@I+hu6W;)hkm|sIhs-8{Ev7t3l zGZBN@0)>Nl*?qPaK5SyIMdBeEj*dDwvF^m7au#P>C4YJKrtafZ!O&fT@N2!A?r@%w zrrdGeT)b;$8`WOAis(e%3$thDxp);Gt7QefK}L7WSuss*sOKJ)Luyf>&M50GqP;`4 z*%K^g0Kz0(VeN7B*rmfuU2mg>^2GDcOcD|3t*zage>CKy&rp1B#jL~vx52sHOEYW1{?6@;|s@<(fF;T~=(0UH0@RcSeww`tq&~ zRimApAIXvw#RBfguU$U|cuxxV-Mh#>C<3?oj7=z@0>q}lj4?zbLJkYu3xPXNQArpC zt~F^5g!RK+vX2pAjqPLpmWEd|GHEreSJq-Hsn7L4ofAQr#OsUMLbMKh&+OOzvrqdm2I59Dl;DxVxVdk=#^#5 z?6<+v_)6P_M~!Ht+A6z@UkT(md~v>z?Yxip3>RlQyhIxJA^N|+?gAodudt6Fnrx_K zG?Q`y#a)A=+Y&ki?(GJ&Fhb*bpUj&5d@Wn@l9+i$r<1DKx}SKPPwwNU7<#Q>4U?dV zFQ3hPVHcC&(kM5GIE1n{)yA9=@AwD~68$Ch`{m$U>aI*q5>v(b@?kaIC?F1^j7>7- z1TdB*r@?|{S+P{VPDAk5lAOra@9+1^RA4c1hbz_Rvt}%y9m7DQY~-yx*4Uv?s6qW= ziMZE<*5KRSHmA%Wx{yvJH1rE9pKzARDY)JIoo#9Lt2PV>T)H_6CCBEuu8z?yK&1RxCj20Z%ja=N z3E8d0k|aXOr_7XnvH5iFJZ*oyvnl`yOFfrYCwO|YqEZRO5y+U{!D}6O>%wLLE8yA@ z^HkbQaxkK7M-xwL_+0X?(bW+O68ZKextLv#N1lV2O6?{*YstYbCwu-WQsR}~y8pae zUtacd)bJVPqTH4YdeEA@(wT=zKae*Ka8I$Q$3`3X{cO;phrJ>b7Wm*YLJTkTdcK~X9dAp zLY9V~EpJHzJ}hkNIm-PDFa}CUm;u_qDJ@!v$lMIUG8g=O_~w--%j_cKs*_yRZMMqLrc~@v*B-sjrS$)ShS$=r|`loGmh~v z$?6YyhU$|?5nmbkX01f^yBIjVf1qd0_u5_75Yrim*LRHUhm&yV%Nk75#nOLRkS%{& z)tAH0-k}Ap5il9sf3ZqqZ9IaUlM5Zhi8KuJYw>JAm>Nc-$d12G6 z|B8x^4m~>%_>kFVnsUOr+UfcoMo@K*fnH(wtOX*>o$sUh>82vyS+x_Iop^Hb%lk~L&YIlo!58IY30DmBq>L~TdQ+Y~GZ6Y`& zl*N%Cp?Ddima+X?3D4oX*CWLV^SdIR*0Z`#f(yQl+`-WEb~&KItQaeDoher|}1(@?J!o zh+I5Xm%o%v=S|4PB56pb+rqpyIXH3FmzX|1EIdQVGYkpFwb1@ayUJf$FAw>6sh6!~+bw z4_UHqcJAW!w(NQ1(R6OP)6)90YZ4@fsiA_;J~iBKOX2c3OR8w&257CaPZ78`&pwUCj8M@pBeL z1i#Vl-zoj8i_;vy5z>&gXZApkjk9-tC9WZBDcjQ zOPk0Ss=ubE1q=Q12H83wH9vFSpj&=2ezNYyIGSk_m>(&Sb?TE}H`mFxlGn^*=Uz@? zp1kqXRSJ&CTC%sB&6&UOEb??e3L<_egv}mr55JjyAFdGUq1n1MWLqh_K?4;?x37nk z+A#hcaj??_g{3#hKq@=t}rr@ zBmN5f_iDg9W0>R(4nGdMRCev#$i%S4xxgE(BVmk;41Kp7&F^1G^nz5w{Dk z2euR)Y}8j0XaeQly|EWEd=Pm=tsYv znBvu(p|98ouk}(R0twE7=uB_TMp?|-(Q8d`K_9b+#W+jyRuZ$5S`$0KjQHx91j+UY zZnwSy!kt5dl-mr0n^A>nJ_pk27KvVa!W!J&)+eTBjnuD<Bj%%y3@Y%LEpVzJ%Y0 zX_pwJ-e4m@lJ?Dd?ZK?f0ER6ud@ZsswzuJG!;S0!Zxr^E`$<>Y&c9?^qOxY998IX+ z`PixBSBfS7+nreHe7n~oXp-88Ml;l@LV(t6nu1fcPs6u#Ll<~@cFm1!qijxX=}_BV zWKD8|yI`pGkM0r1K{ekn)oEw@^}aezy+STqp?X)_qmf-LgiZ=pLvMwFC4{l1okKB%pFwueC3)L|X|tJ`TJtp$REu_H zU>XMH&bMvy3^wJSm(G(iU%LZaZcU8%3!zlYdAFbSPRU-|(IJ81 zb?DM*XH{Fv3b%;Z;tNLi*UDiM#Rf_e8R(jQ9ClNl5LT_R(x~mW-Is+?t^Pxeqw)08 zd(7Vz_{0fLY^hk*PK*Q!p}1?^(L>enWTX|i2xLYaJ&?MKx63@=NI9SHC0Y~q!V>jA zC))T`^Ic?RQ%PTh`mAB^G=q>)?jH`wVQ8eVIAa328GEbtw3+*tqMfOOl~hh1TS7X7roA?;x@D{VhA0}HZ&?^8w`C>T z)+#3LI?&#bGQ}X|PSJ%q(F)ReQ!Kc4I#QM;c%t0@VKBPSqU?WC4K~_c5{cZtRd|og z@{|~^5vWrhVx3wTnlex|&J6r_xhGtJwV4644NuS}i1f_#@Veb;?r{|^--*$*5ZkDE z(udZXwK~FK$1(d=8mRJ|!HDP>=Z=hbj?3-A(5>w4D6XoL|A$rrd` zRE@3pJiK;F6{hvi?K(7IGWi?*S?!GBm%-C-i6A=6Nc5F>x`>WWEygcrg#tAT@#!}_ zHlMQzBg+`VjDp!TYbHyECCw&%N;Pe;L!QnRd3H_pS^P{fug&f3ZWGkDWf`Veh=d*% z;7x&}{{L!woa-b?Wja%UBeC#(q`^pi(Sb5Fsdie&g~e@i4sAAWPQN9b;&FIO}5M&bjaqOEbZzB=mP%Yl&+xRzyxyi4S(Wv!|H+Wo_cY;OQ=0*rJ zv54~ccgVB78VFK2c9m1a3-kU4qCu3i=SxWv3!hHOefyM}jI=7vw~?eWmW@p{e+7zcNiwG#`F(a!{$4eeavz8xpweU z-k0y)bbt#&SUJ*^_CGk%niVDE0MXc9=D8puiyjj86xh@)G#J%A|{H;Gd?mkhj(S+|=+E3%i zom1$|D!-^dYIKR5JU}=ACdcnxI7$ka*$rXtl`Ae<+QErMDp<(gGKUQl;fx;ne}P~D z+NrO;fr~NfX#0a-*PwAVcE$WmnXZoQPF7|V*3h@To|PJf*;~U zC6K$=jEGyE-rHjC=2sz=3F z1)RMfb^0Q&E6I8U_f2LR-+najeqxH3^e~rI4;=;r{TpZp!1>MqPY> zcT+JbQuC*C{ctrb^Za`WR=E2FDwI)hq-{eliVn?@7TQwM{1eSf-^I!F)sjHF!s5#j zvQZaK&9C(qA44xf#SkJ;$_>IVwXv>+OVjYbcP1wx(qm1z>Jey&#u;S{3{(2+XNPTI zWtQ&|3;B^-JBtCjGJhG{GE!{q#6!S$E>n3B~JA-`tP(&l!A94*VR!`L>;p zajp@uR~b6@4&yk+@F`nW;A;>HLZi*Mat8Hs7Ooh={nN*e8soNKt)uc%zupzveg)vU zDR{~9f@AC{r*W6g?9hM=$kh-8X@_^&{G`So_(9sp0hl<-c`?aLvGOj1FPO3B-i&<2 z4j!M54!W;;O=0NAj^MzmjD&+%{8$)nZSpXju`0UvhYJ2K{R)VclTUxNJgB#@ z>SU`&L3#Mp9{?!iK(T+#z-deAy3#KF&U^+yaS_92iSz93Z%?X$sa;v|Cl9y$-$iP1 z^9Qs3ysc6|ircQ4SfD;h`qoVn)nM}nG-Mf)@DAOd=(X0MWmap#6Om=pKkGZ1)6(-;Bx^SjIu_< zW~+r8RI4``$+{Yedq>ehvY}aof8mKpC04Ar)L!>Bvl?}4&1~QwyP~&S!(vEf3A13s z&+~iJ%uja(Gp;R-o4ZMmmgAJ`u;OQ-7Ag%nvAjDq0~b`R5d8@X_}z_0z?amKeJ!cM zMxCOAX~f_M_)Pgp15|IT92FRi_sv%iY#Kn6OCXP%q}fF<+m{3qVRvQQ5m85`XPF5I z-kkD6sI-cOT+mGU9!hiD=AK+PW~(oynGyWYrZ!J_@`Swq=38#vI(3cmfA`jV_A6#4 zHR`dn0qx)`E1ry3@R&)?#E}pMwlN@yzn_SY*lcxh?k&s*9vFos5Y&=*27hjaN;he6j!;&Df?!2_S$ z?t*YXFs+x4&MUVOc-V0KtQ3$m7ogl!Ik~*=Q9eMa=?*NXoO;Ut6sqSO-A{3-w_9t) z>(}t}*!NCv#X`nxkb)IPom@jRvt+dc{{_iW7&PvzU?O%?2s0b6dmww{J;(onjV^ALrUMa}O#KGqci z3l2wvQ8NmdTl%@Uh)jjjV%&ya;_fl82wf8XXi8i9qaqrz%PQ@6@%I1M-j#>tnD*@_ zG+D-ygqLK`-bykWDZ5n4o~8XMNqgzZQVbN3qk5z`uX$r%+E#h9M@kd3lQH2BtN%(_ndNq4lGWUiOI{8d zJLSPg<*=aa4MlbEM}{J;%S^M<`l_Jp9!DyR#+et_dz-qv`N-Jg9~|SSq;0Dc#spRRjn>y8tmdoFRglr(wz;|*24W8eF$t|b0>v8qjkk@9AFWme|2V}Vl_ z##$%$o*Uvgt!J_8r$JG@XSRF!s!;#R8W?lP_R*o_vho54=al6Qr|w#_ExX&z zR$XTYD(AR9tPYuxTG>UlxXj?kbGvLy2aodmzIaZHIY~n||Lb-4iwg}`oXaa*u=`Uz z>r~xw*DY>LZ|W74)MR_F0a>ed7?eNHk2boxKBAHHn*7)8I~RvlIyG+X7O1qd%4}R1 zR5$0l8`=0n#4_`z*!DRAZi%lh56D@4^LSgc(&xV(+S;j3v*N8ycUlIARlL92f2Kul z*^Cc`hm)dTozJvB*kNXzs^81w4Q8d9yX3xhw;C9+KFqCC?(-y{&2EWHw|}$#PF&UP zzYi%AXC4l+i)feE#5TC&g^0SL{y+42JaLgpNp<%103$`)0>>P)x-Y$ze!3M0-fgth zIb<|{zfnQURR5(J_0j|0_Gx3a@srb)qs4~5f9g@kQyDjI{;~?!N3B&Y)Bd==YUMy5 z&)%z>*&7F&m)yuJG(Kk#S5YsmbJJn>CtUkZ`8Mn9ZuiEv7KJ(~gVwtll__7G?)q`B zLr7J`D6_SZu8$u7a8LEbVy9EC|ERk&uG#h6_4oRV^+z@jD|*}YtE&$*{yd~XGiFm_FDZ z&T+LWxt1_y#m&&ubIU6pIQG~y8CuB8BOe~qTCN$b2^Kwy;uGL)slALap zx~ha1Em|hKG)zrb*y?&tvh^&xm)C7==eYj1yFd3mm5_`7Vg2NM+U6#$ht^xa@t1^Q z`Kf`n_2XX(fSt8jy!w1Yw{a&i_M*$XDXvhhq`3wzOad(-nU@w?M>h66utCakyD=Dc60K8 z`ln7_yRVEM_x#<>l$G6$PUaU5Z{GAlOZ{a(IXI>2b@_N`Lu0>I?MHNJps4un%xc?{ zVdM2z8m!E@*z{W;^HS4)pILh>_I=uhO+iY>mV-LSy`LF+Y^H-+&lvu=A_YhQSvctuv9%4Sb9%k}+woN}9S-QSGef!A7s+-H&jrQuZr>HEkdU{-= zcW?JRF`shw<=Mec>&%Z`7rIK7T(P9br7~NCce_PWKh32UdPeu#QuF(mfSu`^5Z^j?2h9IRSy*Y5t0kuMI*|I)80ZYV5g5=VF}x zjJn$<9~nRM)~FgL|*4H|K_1>!tQ~Zhqjv)iqXg zmrpCma(3FaamC}R-+x%=vbcU#pi1>>(4y;k4b9CS*#)lLm)6TXqn;w>jBV;4EhoBq z8x?Olarl(=Z@XSAQn#jdwQYPjdEfm>Q_mItXp@=M_2cC9`TY&g-uzYHwn6Wi%DLKjM7qv12iw7q-zV)#yfkNvL6_6hjeBmmc2ZfmQm@}K z%i^M#%{xvP9q9S^`d{IX{}`__J)b;kaafrGA7MyYaDChT?XQQX|LcBes!8b*tIWIS z`zWL98b{ANvEx*=i>2>~Ed|T1y3DS#BW=7%o3{HMMy#@!vwfr@IQ7lEHj5ToK4~+p z`}Y@OE~ljpZPdHwa==Zsj^27Wt3ef+u?fiO#P-llFUj-wn0ava84a~yB9 ziC|D$LmlJw2qdU=1~tbK8XpRP6tK2@4UrTIDb8b1ugR}6S!0P(*rUD1&dC~T>wvUT z+xP9V#sp;mSkqjcWzAKTeQVL|kTuyTIS1D&$0E2tIQLLCfYG4vT{~arC51w917&SC zcab%m#{uL#Tw{ze!MS{;ntE;MqprDkL@vr@4fE~;*5o_aF_8}1fz*&3L+qVls1sbP zBfy%*xj+H15EOoC?~BKhLOqv=k64Go=AE*}8l~`yF(kyeUDZv+_ZofLnkTX*M?*uq z30;se9L@%?6`T#~fgFePh`Dy5PB32yAp4tv^*~{>5_Q4|6gF%7bx~6McWll>a*v9+ zOk~X_l%h}DuWqs?7iBzH7yL>t#XbIL6A!=^AlC-Rp}oTQ-KZ07z(A0(5&KJwlaD^) z0%!_SBi5nNJVhU|4|D{n5x$GLMxc-2JY7Ly5BtP8d)NSYZvsf4cukP`dIl2S;0%y+ z37b)sa@w60AQ)T<(gxbhIEj4y8+C$fqc_NLgkR#>6o5V=7UVOG8sV21XC(Rv`YxUq zBF@6EnUX@Gc!x3+q+i>V zjoNE$l&ran(gWl;+J4bz0^d|r%mrzW$eFOGC;Euj;82j`ki5?Q!<=3M3EC0~@_G}H z8u9!JM4h+|hJ$(__aGtWdZBJg&z;)xHCon;M;Qci9Bse$%NpilBuIOx<2+(L_d=bZ zofAN68i3Sroizk!fdp+g1bJOM@0Za>Tme17ntcBq{lpWH>zBLL7n5!A8P0yZSD;cOTlFzbHr=HCu$fs!UUWQavW{HUZRiSvospyy3k(F zh3F&ZgSD;IF_J=|h(gJCB#|%rEqp&HYiQ3fkoM3gO~3k}P4HbpJma)Avn7T4*(A#>3g2i?Fi7kJh3_Ip!mlfmLVfP~qt5G1K#n8!UYv!O_3W_zTnbQzivmYhx;NIa_gs|C6-IVslSkyIb7Hgwe*6<9k&97Io zCJ$v0$oPE?YR1q7ZG!W31!)iSMTohWCt?~%-)Sdvrs-EG+5~;_25GaHOZZiZK4LK_ z&JC_H0p|1FZc~z>Z_uy32sbQ?B5$n(ubs`(&y9;wc4I%up zP&cLbI{H|XUq@w4OO(q%o>9y_#}IRIZxZW3vEMlsHNvmgs1xktT(n0!U)Rt_tOi?w z)HDSNaVEH_n~LWs#krxKFH2do7G-UIWy+ckD7F3KCB`{|6(A3^2k8^ZI19i2M4jL} zWM{A@zka0v;NK4Iz?%FTM*;8`Sesu~l0qGmj;M1S?e%<2)@(t!1mxZlXCkVSUwnTc z7^k6NO?iJq0l;UaA;`JJTBZMt&vlSs-a3KQh&3kG#(4A*+=Cs!nr!Zieu8VGHou}I zh58+UW7U)k#*VlEia2Ypu^W;?9Ye7WwQbIlHOxEx*Nl(I`vh4-o9(qU>|=cTfy7I& z1z3~seC`vpSs$c^^ATdMo9d=Cj#y*bHXoNYWhlcy>8<^H$dInZk3bCVM8z8f%mt zYSHi-;RiBzn(+~HouB~V`4bO{HCPvQo*|6SEs)^5a|lSANga@SYW@a^C!koXoKI6j ze~A5{m|r`FlO%=u9YAQb_fCIVGX-UBe$gKy9$W^}PUeYzaevU}7?9xK1w%mEB5W2p zeTP1Rdq=Dd-b)DIZ>yUM`ez6-Ut&Gef9fZLL=UhdSW`}IC;*(m+Wg9q6beO8l$=lV zoaZIZHGma>>$te2NP$$-dOF-s`v1Z;yzCNH% zyavaD97EgpyXYf0ztCv=?k*`5iu)*Q%hyk`#v7$rV`A?JoB3`)aIOT9e)HU*&FrJ^ zd

^K!1=nQ$yc{rUZ4O4LA(c)QEGwngW3Sxq#F#UmQooc_8Y0GF37h{xoyY^Z ze`@mU6a_#ZP>ij8wtOoo)HaL1FT^~|3;klgiN&A^$n~Xt&hvRh_qfM zmk-AGi13Rvn+>a_Q-hZo1)q?hknsO@qeof at the -# end. Used for displaying the number of scripts without including all the -# Inner Classes. -func get_non_inner_class_script_count(): - var count = 0 - var unique_scripts = {} - for i in range(_scripts.size()): - var ext_loc = _scripts[i].name.find_last('.gd.') - if(ext_loc == -1): - unique_scripts[_scripts[i].name] = 1 - else: - unique_scripts[_scripts[i].name.substr(0, ext_loc + 3)] = 1 - return unique_scripts.keys().size() - -func get_totals(): - var totals = { - passing = 0, - pending = 0, - failing = 0, - tests = 0, - scripts = 0 - } - - for s in range(_scripts.size()): - totals.passing += _scripts[s].get_pass_count() - totals.pending += _scripts[s].get_pending_count() - totals.failing += _scripts[s].get_fail_count() - totals.tests += _scripts[s]._test_order.size() - - totals.scripts = get_non_inner_class_script_count() - - return totals - -func get_summary_text(): - var _totals = get_totals() - - var to_return = '' - for s in range(_scripts.size()): - if(_scripts[s].get_fail_count() > 0 or _scripts[s].get_pending_count() > 0): - to_return += _scripts[s].name + "\n" - 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): - to_return += str(' - ', tname, "\n", test.to_s()) - - var header = "*** Totals ***\n" - header += str(' scripts: ', get_non_inner_class_script_count(), "\n") - header += str(' tests: ', _totals.tests, "\n") - header += str(' passing asserts: ', _totals.passing, "\n") - header += str(' failing asserts: ',_totals.failing, "\n") - header += str(' pending: ', _totals.pending, "\n") - - return to_return + "\n" + header diff --git a/addons/gut/test.gd b/addons/gut/test.gd deleted file mode 100644 index 1fd6954..0000000 --- a/addons/gut/test.gd +++ /dev/null @@ -1,1088 +0,0 @@ -################################################################################ -#(G)odot (U)nit (T)est class -# -################################################################################ -#The MIT License (MIT) -#===================== -# -#Copyright (c) 2019 Tom "Butch" Wesley -# -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: -# -#The above copyright notice and this permission notice shall be included in -#all copies or substantial portions of the Software. -# -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -#THE SOFTWARE. -# -################################################################################ -# View readme for usage details. -# -# Version - see gut.gd -################################################################################ -# Class that all test scripts must extend. -# -# This provides all the asserts and other testing features. Test scripts are -# run by the Gut class in gut.gd -################################################################################ -extends Node - -# ------------------------------------------------------------------------------ -# Helper class to hold info for objects to double. This extracts info and has -# some convenience methods. This is key in being able to make the "smart double" -# method which makes doubling much easier for the user. -# ------------------------------------------------------------------------------ -class DoubleInfo: - var path - var subpath - var strategy - var make_partial - var extension - var _utils = load('res://addons/gut/utils.gd').new() - var _is_native = false - - # Flexible init method. p2 can be subpath or stategy unless p3 is - # specified, then p2 must be subpath and p3 is strategy. - # - # Examples: - # (object_to_double) - # (object_to_double, subpath) - # (object_to_double, strategy) - # (object_to_double, subpath, strategy) - func _init(thing, p2=null, p3=null): - strategy = p2 - - if(typeof(p2) == TYPE_STRING): - strategy = p3 - subpath = p2 - - if(typeof(thing) == TYPE_OBJECT): - if(_utils.is_native_class(thing)): - path = thing - _is_native = true - extension = 'native_class_not_used' - else: - path = thing.resource_path - else: - path = thing - - if(!_is_native): - extension = path.get_extension() - - func is_scene(): - return extension == 'tscn' - - func is_script(): - return extension == 'gd' - - func is_native(): - return _is_native - -# ------------------------------------------------------------------------------ -# Begin test.gd -# ------------------------------------------------------------------------------ - -# constant for signal when calling yield_for -const YIELD = 'timeout' - -# Need a reference to the instance that is running the tests. This -# is set by the gut class when it runs the tests. This gets you -# access to the asserts in the tests you write. -var gut = null -var passed = false -var failed = false -var _disable_strict_datatype_checks = false -# Holds all the text for a test's fail/pass. This is used for testing purposes -# to see the text of a failed sub-test in test_test.gd -var _fail_pass_text = [] - -# Hash containing all the built in types in Godot. This provides an English -# name for the types that corosponds with the type constants defined in the -# engine. This is used for priting out messages when comparing types fails. -var types = {} - -func _init_types_dictionary(): - types[TYPE_NIL] = 'TYPE_NIL' - types[TYPE_BOOL] = 'Bool' - types[TYPE_INT] = 'Int' - types[TYPE_REAL] = 'Float/Real' - types[TYPE_STRING] = 'String' - types[TYPE_VECTOR2] = 'Vector2' - types[TYPE_RECT2] = 'Rect2' - types[TYPE_VECTOR3] = 'Vector3' - #types[8] = 'Matrix32' - types[TYPE_PLANE] = 'Plane' - types[TYPE_QUAT] = 'QUAT' - types[TYPE_AABB] = 'AABB' - #types[12] = 'Matrix3' - types[TYPE_TRANSFORM] = 'Transform' - types[TYPE_COLOR] = 'Color' - #types[15] = 'Image' - types[TYPE_NODE_PATH] = 'Node Path' - types[TYPE_RID] = 'RID' - types[TYPE_OBJECT] = 'TYPE_OBJECT' - #types[19] = 'TYPE_INPUT_EVENT' - types[TYPE_DICTIONARY] = 'Dictionary' - types[TYPE_ARRAY] = 'Array' - types[TYPE_RAW_ARRAY] = 'TYPE_RAW_ARRAY' - types[TYPE_INT_ARRAY] = 'TYPE_INT_ARRAY' - types[TYPE_REAL_ARRAY] = 'TYPE_REAL_ARRAY' - types[TYPE_STRING_ARRAY] = 'TYPE_STRING_ARRAY' - types[TYPE_VECTOR2_ARRAY] = 'TYPE_VECTOR2_ARRAY' - types[TYPE_VECTOR3_ARRAY] = 'TYPE_VECTOR3_ARRAY' - types[TYPE_COLOR_ARRAY] = 'TYPE_COLOR_ARRAY' - types[TYPE_MAX] = 'TYPE_MAX' - -const EDITOR_PROPERTY = PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_DEFAULT -const VARIABLE_PROPERTY = PROPERTY_USAGE_SCRIPT_VARIABLE - -# Summary counts for the test. -var _summary = { - asserts = 0, - passed = 0, - failed = 0, - tests = 0, - pending = 0 -} - -# This is used to watch signals so we can make assertions about them. -var _signal_watcher = load('res://addons/gut/signal_watcher.gd').new() - -# Convenience copy of _utils.DOUBLE_STRATEGY -var DOUBLE_STRATEGY = null -var _utils = load('res://addons/gut/utils.gd').new() -var _lgr = _utils.get_logger() - -func _init(): - _init_types_dictionary() - DOUBLE_STRATEGY = _utils.DOUBLE_STRATEGY # yes, this is right - -# ------------------------------------------------------------------------------ -# Fail an assertion. Causes test and script to fail as well. -# ------------------------------------------------------------------------------ -func _fail(text): - _summary.asserts += 1 - _summary.failed += 1 - var msg = 'FAILED: ' + text - _fail_pass_text.append(msg) - if(gut): - gut.p(msg, gut.LOG_LEVEL_FAIL_ONLY) - gut._fail(text) - -# ------------------------------------------------------------------------------ -# Pass an assertion. -# ------------------------------------------------------------------------------ -func _pass(text): - _summary.asserts += 1 - _summary.passed += 1 - var msg = "PASSED: " + text - _fail_pass_text.append(msg) - if(gut): - gut.p(msg, gut.LOG_LEVEL_ALL_ASSERTS) - gut._pass(text) - -# ------------------------------------------------------------------------------ -# Checks if the datatypes passed in match. If they do not then this will cause -# a fail to occur. If they match then TRUE is returned, FALSE if not. This is -# used in all the assertions that compare values. -# ------------------------------------------------------------------------------ -func _do_datatypes_match__fail_if_not(got, expected, text): - var passed = true - - if(!_disable_strict_datatype_checks): - var got_type = typeof(got) - var expect_type = typeof(expected) - if(got_type != expect_type and got != null and expected != null): - # If we have a mismatch between float and int (types 2 and 3) then - # print out a warning but do not fail. - if([2, 3].has(got_type) and [2, 3].has(expect_type)): - _lgr.warn(str('Warn: Float/Int comparison. Got ', types[got_type], ' but expected ', types[expect_type])) - else: - _fail('Cannot compare ' + types[got_type] + '[' + str(got) + '] to ' + types[expect_type] + '[' + str(expected) + ']. ' + text) - passed = false - - return passed - -# ------------------------------------------------------------------------------ -# Create a string that lists all the methods that were called on an spied -# instance. -# ------------------------------------------------------------------------------ -func _get_desc_of_calls_to_instance(inst): - var BULLET = ' * ' - var calls = gut.get_spy().get_call_list_as_string(inst) - # indent all the calls - calls = BULLET + calls.replace("\n", "\n" + BULLET) - # remove trailing newline and bullet - calls = calls.substr(0, calls.length() - BULLET.length() - 1) - return "Calls made on " + str(inst) + "\n" + calls - -# ------------------------------------------------------------------------------ -# Signal assertion helper. Do not call directly, use _can_make_signal_assertions -# ------------------------------------------------------------------------------ -func _fail_if_does_not_have_signal(object, signal_name): - var did_fail = false - if(!_signal_watcher.does_object_have_signal(object, signal_name)): - _fail(str('Object ', object, ' does not have the signal [', signal_name, ']')) - did_fail = true - return did_fail -# ------------------------------------------------------------------------------ -# Signal assertion helper. Do not call directly, use _can_make_signal_assertions -# ------------------------------------------------------------------------------ -func _fail_if_not_watching(object): - var did_fail = false - if(!_signal_watcher.is_watching_object(object)): - _fail(str('Cannot make signal assertions because the object ', object, \ - ' is not being watched. Call watch_signals(some_object) to be able to make assertions about signals.')) - did_fail = true - return did_fail - -# ------------------------------------------------------------------------------ -# Returns text that contains original text and a list of all the signals that -# were emitted for the passed in object. -# ------------------------------------------------------------------------------ -func _get_fail_msg_including_emitted_signals(text, object): - return str(text," (Signals emitted: ", _signal_watcher.get_signals_emitted(object), ")") - -# ------------------------------------------------------------------------------ -# This validates that parameters is an array and generates a specific error -# and a failure with a specific message -# ------------------------------------------------------------------------------ -func _fail_if_parameters_not_array(parameters): - var invalid = parameters != null and typeof(parameters) != TYPE_ARRAY - if(invalid): - _lgr.error('The "parameters" parameter must be an array of expected parameter values.') - _fail('Cannot compare paramter values because an array was not passed.') - return invalid -# ####################### -# Virtual Methods -# ####################### - -# alias for prerun_setup -func before_all(): - pass - -# alias for setup -func before_each(): - pass - -# alias for postrun_teardown -func after_all(): - pass - -# alias for teardown -func after_each(): - pass - -# ####################### -# Public -# ####################### - -func get_logger(): - return _lgr - -func set_logger(logger): - _lgr = logger - - -# ####################### -# Asserts -# ####################### - -# ------------------------------------------------------------------------------ -# Asserts that the expected value equals the value got. -# ------------------------------------------------------------------------------ -func assert_eq(got, expected, text=""): - var disp = "[" + str(got) + "] expected to equal [" + str(expected) + "]: " + text - if(_do_datatypes_match__fail_if_not(got, expected, text)): - if(expected != got): - _fail(disp) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Asserts that the value got does not equal the "not expected" value. -# ------------------------------------------------------------------------------ -func assert_ne(got, not_expected, text=""): - var disp = "[" + str(got) + "] expected to be anything except [" + str(not_expected) + "]: " + text - if(_do_datatypes_match__fail_if_not(got, not_expected, text)): - if(got == not_expected): - _fail(disp) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Asserts that the expected value almost equals the value got. -# ------------------------------------------------------------------------------ -func assert_almost_eq(got, expected, error_interval, text=''): - var disp = "[" + str(got) + "] expected to equal [" + str(expected) + "] +/- [" + str(error_interval) + "]: " + text - if(_do_datatypes_match__fail_if_not(got, expected, text) and _do_datatypes_match__fail_if_not(got, error_interval, text)): - if(got < (expected - error_interval) or got > (expected + error_interval)): - _fail(disp) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Asserts that the expected value does not almost equal the value got. -# ------------------------------------------------------------------------------ -func assert_almost_ne(got, not_expected, error_interval, text=''): - var disp = "[" + str(got) + "] expected to not equal [" + str(not_expected) + "] +/- [" + str(error_interval) + "]: " + text - if(_do_datatypes_match__fail_if_not(got, not_expected, text) and _do_datatypes_match__fail_if_not(got, error_interval, text)): - if(got < (not_expected - error_interval) or got > (not_expected + error_interval)): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Asserts got is greater than expected -# ------------------------------------------------------------------------------ -func assert_gt(got, expected, text=""): - var disp = "[" + str(got) + "] expected to be > than [" + str(expected) + "]: " + text - if(_do_datatypes_match__fail_if_not(got, expected, text)): - if(got > expected): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Asserts got is less than expected -# ------------------------------------------------------------------------------ -func assert_lt(got, expected, text=""): - var disp = "[" + str(got) + "] expected to be < than [" + str(expected) + "]: " + text - if(_do_datatypes_match__fail_if_not(got, expected, text)): - if(got < expected): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# asserts that got is true -# ------------------------------------------------------------------------------ -func assert_true(got, text=""): - if(!got): - _fail(text) - else: - _pass(text) - -# ------------------------------------------------------------------------------ -# Asserts that got is false -# ------------------------------------------------------------------------------ -func assert_false(got, text=""): - if(got): - _fail(text) - else: - _pass(text) - -# ------------------------------------------------------------------------------ -# Asserts value is between (inclusive) the two expected values. -# ------------------------------------------------------------------------------ -func assert_between(got, expect_low, expect_high, text=""): - var disp = "[" + str(got) + "] expected to be between [" + str(expect_low) + "] and [" + str(expect_high) + "]: " + text - - if(_do_datatypes_match__fail_if_not(got, expect_low, text) and _do_datatypes_match__fail_if_not(got, expect_high, text)): - if(expect_low > expect_high): - disp = "INVALID range. [" + str(expect_low) + "] is not less than [" + str(expect_high) + "]" - _fail(disp) - else: - if(got < expect_low or got > expect_high): - _fail(disp) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Uses the 'has' method of the object passed in to determine if it contains -# the passed in element. -# ------------------------------------------------------------------------------ -func assert_has(obj, element, text=""): - var disp = str('Expected [', obj, '] to contain value: [', element, ']: ', text) - if(obj.has(element)): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func assert_does_not_have(obj, element, text=""): - var disp = str('Expected [', obj, '] to NOT contain value: [', element, ']: ', text) - if(obj.has(element)): - _fail(disp) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Asserts that a file exists -# ------------------------------------------------------------------------------ -func assert_file_exists(file_path): - var disp = 'expected [' + file_path + '] to exist.' - var f = File.new() - if(f.file_exists(file_path)): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Asserts that a file should not exist -# ------------------------------------------------------------------------------ -func assert_file_does_not_exist(file_path): - var disp = 'expected [' + file_path + '] to NOT exist' - var f = File.new() - if(!f.file_exists(file_path)): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Asserts the specified file is empty -# ------------------------------------------------------------------------------ -func assert_file_empty(file_path): - var disp = 'expected [' + file_path + '] to be empty' - var f = File.new() - if(f.file_exists(file_path) and gut.is_file_empty(file_path)): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Asserts the specified file is not empty -# ------------------------------------------------------------------------------ -func assert_file_not_empty(file_path): - var disp = 'expected [' + file_path + '] to contain data' - if(!gut.is_file_empty(file_path)): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Asserts the object has the specified method -# ------------------------------------------------------------------------------ -func assert_has_method(obj, method): - assert_true(obj.has_method(method), 'Should have method: ' + method) - -# Old deprecated method name -func assert_get_set_methods(obj, property, default, set_to): - _lgr.deprecated('assert_get_set_methods', 'assert_accessors') - assert_accessors(obj, property, default, set_to) - -# ------------------------------------------------------------------------------ -# Verifies the object has get and set methods for the property passed in. The -# property isn't tied to anything, just a name to be appended to the end of -# get_ and set_. Asserts the get_ and set_ methods exist, if not, it stops there. -# If they exist then it asserts get_ returns the expected default then calls -# set_ and asserts get_ has the value it was set to. -# ------------------------------------------------------------------------------ -func assert_accessors(obj, property, default, set_to): - var fail_count = _summary.failed - var get = 'get_' + property - var set = 'set_' + property - assert_has_method(obj, get) - assert_has_method(obj, set) - # SHORT CIRCUIT - if(_summary.failed > fail_count): - return - assert_eq(obj.call(get), default, 'It should have the expected default value.') - obj.call(set, set_to) - assert_eq(obj.call(get), set_to, 'The set value should have been returned.') - - -# --------------------------------------------------------------------------- -# Property search helper. Used to retrieve Dictionary of specified property -# from passed object. Returns null if not found. -# If provided, property_usage constrains the type of property returned by -# passing either: -# EDITOR_PROPERTY for properties defined as: export(int) var some_value -# VARIABLE_PROPERTY for properties defunded as: var another_value -# --------------------------------------------------------------------------- -func _find_object_property(obj, property_name, property_usage=null): - var result = null - var found = false - var properties = obj.get_property_list() - - while !found and !properties.empty(): - var property = properties.pop_back() - if property['name'] == property_name: - if property_usage == null or property['usage'] == property_usage: - result = property - found = true - return result - -# ------------------------------------------------------------------------------ -# Asserts a class exports a variable. -# ------------------------------------------------------------------------------ -func assert_exports(obj, property_name, type): - var disp = 'expected %s to have editor property [%s]' % [obj, property_name] - var property = _find_object_property(obj, property_name, EDITOR_PROPERTY) - if property != null: - disp += ' of type [%s]. Got type [%s].' % [types[type], types[property['type']]] - if property['type'] == type: - _pass(disp) - else: - _fail(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Signal assertion helper. -# -# Verifies that the object and signal are valid for making signal assertions. -# This will fail with specific messages that indicate why they are not valid. -# This returns true/false to indicate if the object and signal are valid. -# ------------------------------------------------------------------------------ -func _can_make_signal_assertions(object, signal_name): - return !(_fail_if_not_watching(object) or _fail_if_does_not_have_signal(object, signal_name)) - -# ------------------------------------------------------------------------------ -# Watch the signals for an object. This must be called before you can make -# any assertions about the signals themselves. -# ------------------------------------------------------------------------------ -func watch_signals(object): - _signal_watcher.watch_signals(object) - -# ------------------------------------------------------------------------------ -# Asserts that a signal has been emitted at least once. -# -# This will fail with specific messages if the object is not being watched or -# the object does not have the specified signal -# ------------------------------------------------------------------------------ -func assert_signal_emitted(object, signal_name, text=""): - var disp = str('Expected object ', object, ' to have emitted signal [', signal_name, ']: ', text) - if(_can_make_signal_assertions(object, signal_name)): - if(_signal_watcher.did_emit(object, signal_name)): - _pass(disp) - else: - _fail(_get_fail_msg_including_emitted_signals(disp, object)) - -# ------------------------------------------------------------------------------ -# Asserts that a signal has not been emitted. -# -# This will fail with specific messages if the object is not being watched or -# the object does not have the specified signal -# ------------------------------------------------------------------------------ -func assert_signal_not_emitted(object, signal_name, text=""): - var disp = str('Expected object ', object, ' to NOT emit signal [', signal_name, ']: ', text) - if(_can_make_signal_assertions(object, signal_name)): - if(_signal_watcher.did_emit(object, signal_name)): - _fail(disp) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Asserts that a signal was fired with the specified parameters. The expected -# parameters should be passed in as an array. An optional index can be passed -# when a signal has fired more than once. The default is to retrieve the most -# recent emission of the signal. -# -# This will fail with specific messages if the object is not being watched or -# the object does not have the specified signal -# ------------------------------------------------------------------------------ -func assert_signal_emitted_with_parameters(object, signal_name, parameters, index=-1): - var disp = str('Expected object ', object, ' to emit signal [', signal_name, '] with parameters ', parameters, ', got ') - if(_can_make_signal_assertions(object, signal_name)): - if(_signal_watcher.did_emit(object, signal_name)): - var parms_got = _signal_watcher.get_signal_parameters(object, signal_name, index) - if(parameters == parms_got): - _pass(str(disp, parms_got)) - else: - _fail(str(disp, parms_got)) - else: - var text = str('Object ', object, ' did not emit signal [', signal_name, ']') - _fail(_get_fail_msg_including_emitted_signals(text, object)) - -# ------------------------------------------------------------------------------ -# Assert that a signal has been emitted a specific number of times. -# -# This will fail with specific messages if the object is not being watched or -# the object does not have the specified signal -# ------------------------------------------------------------------------------ -func assert_signal_emit_count(object, signal_name, times, text=""): - - if(_can_make_signal_assertions(object, signal_name)): - var count = _signal_watcher.get_emit_count(object, signal_name) - var disp = str('Expected the signal [', signal_name, '] emit count of [', count, '] to equal [', times, ']: ', text) - if(count== times): - _pass(disp) - else: - _fail(_get_fail_msg_including_emitted_signals(disp, object)) - -# ------------------------------------------------------------------------------ -# Assert that the passed in object has the specified signal -# ------------------------------------------------------------------------------ -func assert_has_signal(object, signal_name, text=""): - var disp = str('Expected object ', object, ' to have signal [', signal_name, ']: ', text) - if(_signal_watcher.does_object_have_signal(object, signal_name)): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Returns the number of times a signal was emitted. -1 returned if the object -# is not being watched. -# ------------------------------------------------------------------------------ -func get_signal_emit_count(object, signal_name): - return _signal_watcher.get_emit_count(object, signal_name) - -# ------------------------------------------------------------------------------ -# Get the parmaters of a fired signal. If the signal was not fired null is -# returned. You can specify an optional index (use get_signal_emit_count to -# determine the number of times it was emitted). The default index is the -# latest time the signal was fired (size() -1 insetead of 0). The parameters -# returned are in an array. -# ------------------------------------------------------------------------------ -func get_signal_parameters(object, signal_name, index=-1): - return _signal_watcher.get_signal_parameters(object, signal_name, index) - -# ------------------------------------------------------------------------------ -# Get the parameters for a method call to a doubled object. By default it will -# return the most recent call. You can optionally specify an index. -# -# Returns: -# * an array of parameter values if a call the method was found -# * null when a call to the method was not found or the index specified was -# invalid. -# ------------------------------------------------------------------------------ -func get_call_parameters(object, method_name, index=-1): - var to_return = null - if(_utils.is_double(object)): - to_return = gut.get_spy().get_call_parameters(object, method_name, index) - else: - _lgr.error('You must pass a doulbed object to get_call_parameters.') - - return to_return - -# ------------------------------------------------------------------------------ -# Assert that object is an instance of a_class -# ------------------------------------------------------------------------------ -func assert_extends(object, a_class, text=''): - _lgr.deprecated('assert_extends', 'assert_is') - assert_is(object, a_class, text) - -# Alias for assert_extends -func assert_is(object, a_class, text=''): - var disp = str('Expected [', object, '] to be type of [', a_class, ']: ', text) - var NATIVE_CLASS = 'GDScriptNativeClass' - var GDSCRIPT_CLASS = 'GDScript' - var bad_param_2 = 'Parameter 2 must be a Class (like Node2D or Label). You passed ' - - if(typeof(object) != TYPE_OBJECT): - _fail(str('Parameter 1 must be an instance of an object. You passed: ', types[typeof(object)])) - elif(typeof(a_class) != TYPE_OBJECT): - _fail(str(bad_param_2, types[typeof(a_class)])) - else: - disp = str('Expected [', object.get_class(), '] to extend [', a_class.get_class(), ']: ', text) - if(a_class.get_class() != NATIVE_CLASS and a_class.get_class() != GDSCRIPT_CLASS): - _fail(str(bad_param_2, a_class.get_class(), ' ', types[typeof(a_class)])) - else: - if(object is a_class): - _pass(disp) - else: - _fail(disp) - - -# ------------------------------------------------------------------------------ -# Assert that text contains given search string. -# The match_case flag determines case sensitivity. -# ------------------------------------------------------------------------------ -func assert_string_contains(text, search, match_case=true): - var empty_search = 'Expected text and search strings to be non-empty. You passed \'%s\' and \'%s\'.' - var disp = 'Expected \'%s\' to contain \'%s\', match_case=%s' % [text, search, match_case] - if(text == '' or search == ''): - _fail(empty_search % [text, search]) - elif(match_case): - if(text.find(search) == -1): - _fail(disp) - else: - _pass(disp) - else: - if(text.to_lower().find(search.to_lower()) == -1): - _fail(disp) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Assert that text starts with given search string. -# match_case flag determines case sensitivity. -# ------------------------------------------------------------------------------ -func assert_string_starts_with(text, search, match_case=true): - var empty_search = 'Expected text and search strings to be non-empty. You passed \'%s\' and \'%s\'.' - var disp = 'Expected \'%s\' to start with \'%s\', match_case=%s' % [text, search, match_case] - if(text == '' or search == ''): - _fail(empty_search % [text, search]) - elif(match_case): - if(text.find(search) == 0): - _pass(disp) - else: - _fail(disp) - else: - if(text.to_lower().find(search.to_lower()) == 0): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Assert that text ends with given search string. -# match_case flag determines case sensitivity. -# ------------------------------------------------------------------------------ -func assert_string_ends_with(text, search, match_case=true): - var empty_search = 'Expected text and search strings to be non-empty. You passed \'%s\' and \'%s\'.' - var disp = 'Expected \'%s\' to end with \'%s\', match_case=%s' % [text, search, match_case] - var required_index = len(text) - len(search) - if(text == '' or search == ''): - _fail(empty_search % [text, search]) - elif(match_case): - if(text.find(search) == required_index): - _pass(disp) - else: - _fail(disp) - else: - if(text.to_lower().find(search.to_lower()) == required_index): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Assert that a method was called on an instance of a doubled class. If -# parameters are supplied then the params passed in when called must match. -# TODO make 3rd parameter "param_or_text" and add fourth parameter of "text" and -# then work some magic so this can have a "text" parameter without being -# annoying. -# ------------------------------------------------------------------------------ -func assert_called(inst, method_name, parameters=null): - var disp = str('Expected [',method_name,'] to have been called on ',inst) - - if(_fail_if_parameters_not_array(parameters)): - return - - if(!_utils.is_double(inst)): - _fail('You must pass a doubled instance to assert_called. Check the wiki for info on using double.') - else: - if(gut.get_spy().was_called(inst, method_name, parameters)): - _pass(disp) - else: - if(parameters != null): - disp += str(' with parameters ', parameters) - _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) - -# ------------------------------------------------------------------------------ -# Assert that a method was not called on an instance of a doubled class. If -# parameters are specified then this will only fail if it finds a call that was -# sent matching parameters. -# ------------------------------------------------------------------------------ -func assert_not_called(inst, method_name, parameters=null): - var disp = str('Expected [', method_name, '] to NOT have been called on ', inst) - - if(_fail_if_parameters_not_array(parameters)): - return - - if(!_utils.is_double(inst)): - _fail('You must pass a doubled instance to assert_not_called. Check the wiki for info on using double.') - else: - if(gut.get_spy().was_called(inst, method_name, parameters)): - if(parameters != null): - disp += str(' with parameters ', parameters) - _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) - else: - _pass(disp) - -# ------------------------------------------------------------------------------ -# Assert that a method on an instance of a doubled class was called a number -# of times. If parameters are specified then only calls with matching -# parameter values will be counted. -# ------------------------------------------------------------------------------ -func assert_call_count(inst, method_name, expected_count, parameters=null): - var count = gut.get_spy().call_count(inst, method_name, parameters) - - if(_fail_if_parameters_not_array(parameters)): - return - - var param_text = '' - if(parameters): - param_text = ' with parameters ' + str(parameters) - var disp = 'Expected [%s] on %s to be called [%s] times%s. It was called [%s] times.' - disp = disp % [method_name, inst, expected_count, param_text, count] - - if(!_utils.is_double(inst)): - _fail('You must pass a doubled instance to assert_call_count. Check the wiki for info on using double.') - else: - if(count == expected_count): - _pass(disp) - else: - _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) - -# ------------------------------------------------------------------------------ -# Asserts the passed in value is null -# ------------------------------------------------------------------------------ -func assert_null(got, text=''): - var disp = str('Expected [', got, '] to be NULL: ', text) - if(got == null): - _pass(disp) - else: - _fail(disp) - -# ------------------------------------------------------------------------------ -# Asserts the passed in value is null -# ------------------------------------------------------------------------------ -func assert_not_null(got, text=''): - var disp = str('Expected [', got, '] to be anything but NULL: ', text) - if(got == null): - _fail(disp) - else: - _pass(disp) - -# ----------------------------------------------------------------------------- -# Asserts object has been freed from memory -# We pass in a title (since if it is freed, we lost all identity data) -# ----------------------------------------------------------------------------- -func assert_freed(obj, title): - assert_true(not is_instance_valid(obj), "Object %s is freed" % title) - -# ------------------------------------------------------------------------------ -# Asserts Object has not been freed from memory -# ----------------------------------------------------------------------------- -func assert_not_freed(obj, title): - assert_true(is_instance_valid(obj), "Object %s is not freed" % title) - -# ------------------------------------------------------------------------------ -# Mark the current test as pending. -# ------------------------------------------------------------------------------ -func pending(text=""): - _summary.pending += 1 - if(gut): - if(text == ""): - gut.p("Pending") - else: - gut.p("Pending: " + text) - gut._pending(text) - -# ------------------------------------------------------------------------------ -# Returns the number of times a signal was emitted. -1 returned if the object -# is not being watched. -# ------------------------------------------------------------------------------ - -# ------------------------------------------------------------------------------ -# Yield for the time sent in. The optional message will be printed when -# Gut detects the yield. When the time expires the YIELD signal will be -# emitted. -# ------------------------------------------------------------------------------ -func yield_for(time, msg=''): - return gut.set_yield_time(time, msg) - -# ------------------------------------------------------------------------------ -# Yield to a signal or a maximum amount of time, whichever comes first. When -# the conditions are met the YIELD signal will be emitted. -# ------------------------------------------------------------------------------ -func yield_to(obj, signal_name, max_wait, msg=''): - watch_signals(obj) - gut.set_yield_signal_or_time(obj, signal_name, max_wait, msg) - - return gut - -# ------------------------------------------------------------------------------ -# Ends a test that had a yield in it. You only need to use this if you do -# not make assertions after a yield. -# ------------------------------------------------------------------------------ -func end_test(): - _lgr.deprecated('end_test is no longer necessary, you can remove it.') - #gut.end_yielded_test() - -func get_summary(): - return _summary - -func get_fail_count(): - return _summary.failed - -func get_pass_count(): - return _summary.passed - -func get_pending_count(): - return _summary.pending - -func get_assert_count(): - return _summary.asserts - -func clear_signal_watcher(): - _signal_watcher.clear() - -func get_double_strategy(): - return gut.get_doubler().get_strategy() - -func set_double_strategy(double_strategy): - gut.get_doubler().set_strategy(double_strategy) - -func pause_before_teardown(): - gut.pause_before_teardown() -# ------------------------------------------------------------------------------ -# Convert the _summary dictionary into text -# ------------------------------------------------------------------------------ -func get_summary_text(): - var to_return = get_script().get_path() + "\n" - to_return += str(' ', _summary.passed, ' of ', _summary.asserts, ' passed.') - if(_summary.pending > 0): - to_return += str("\n ", _summary.pending, ' pending') - if(_summary.failed > 0): - to_return += str("\n ", _summary.failed, ' failed.') - return to_return - -# ------------------------------------------------------------------------------ -# Double a script, inner class, or scene using a path or a loaded script/scene. -# -# -# ------------------------------------------------------------------------------ - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func _smart_double(double_info): - var override_strat = _utils.nvl(double_info.strategy, gut.get_doubler().get_strategy()) - var to_return = null - - if(double_info.is_scene()): - if(double_info.make_partial): - to_return = gut.get_doubler().partial_double_scene(double_info.path, override_strat) - else: - to_return = gut.get_doubler().double_scene(double_info.path, override_strat) - - elif(double_info.is_native()): - if(double_info.make_partial): - to_return = gut.get_doubler().partial_double_gdnative(double_info.path) - else: - to_return = gut.get_doubler().double_gdnative(double_info.path) - - elif(double_info.is_script()): - if(double_info.subpath == null): - if(double_info.make_partial): - to_return = gut.get_doubler().partial_double(double_info.path, override_strat) - else: - to_return = gut.get_doubler().double(double_info.path, override_strat) - else: - if(double_info.make_partial): - to_return = gut.get_doubler().partial_double_inner(double_info.path, double_info.subpath, override_strat) - else: - to_return = gut.get_doubler().double_inner(double_info.path, double_info.subpath, override_strat) - return to_return - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func double(thing, p2=null, p3=null): - var double_info = DoubleInfo.new(thing, p2, p3) - double_info.make_partial = false - - return _smart_double(double_info) - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -func partial_double(thing, p2=null, p3=null): - var double_info = DoubleInfo.new(thing, p2, p3) - double_info.make_partial = true - - return _smart_double(double_info) - - -# ------------------------------------------------------------------------------ -# Specifically double a scene -# ------------------------------------------------------------------------------ -func double_scene(path, strategy=null): - var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) - return gut.get_doubler().double_scene(path, override_strat) - -# ------------------------------------------------------------------------------ -# Specifically double a script -# ------------------------------------------------------------------------------ -func double_script(path, strategy=null): - var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) - return gut.get_doubler().double(path, override_strat) - -# ------------------------------------------------------------------------------ -# Specifically double an Inner class in a a script -# ------------------------------------------------------------------------------ -func double_inner(path, subpath, strategy=null): - var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) - return gut.get_doubler().double_inner(path, subpath, override_strat) - -# ------------------------------------------------------------------------------ -# Add a method that the doubler will ignore. You can pass this the path to a -# script or scene or a loaded script or scene. When running tests, these -# ignores are cleared after every test. -# ------------------------------------------------------------------------------ -func ignore_method_when_doubling(thing, method_name): - var double_info = DoubleInfo.new(thing) - var path = double_info.path - - if(double_info.is_scene()): - var inst = thing.instance() - if(inst.get_script()): - path = inst.get_script().get_path() - - gut.get_doubler().add_ignored_method(path, method_name) - -# ------------------------------------------------------------------------------ -# Stub something. -# -# Parameters -# 1: the thing to stub, a file path or a instance or a class -# 2: either an inner class subpath or the method name -# 3: the method name if an inner class subpath was specified -# NOTE: right now we cannot stub inner classes at the path level so this should -# only be called with two parameters. I did the work though so I'm going -# to leave it but not update the wiki. -# ------------------------------------------------------------------------------ -func stub(thing, p2, p3=null): - var method_name = p2 - var subpath = null - if(p3 != null): - subpath = p2 - method_name = p3 - var sp = _utils.StubParams.new(thing, method_name, subpath) - gut.get_stubber().add_stub(sp) - return sp - -# ------------------------------------------------------------------------------ -# convenience wrapper. -# ------------------------------------------------------------------------------ -func simulate(obj, times, delta): - gut.simulate(obj, times, delta) - -# ------------------------------------------------------------------------------ -# Replace the node at base_node.get_node(path) with with_this. All references -# to the node via $ and get_node(...) will now return with_this. with_this will -# get all the groups that the node that was replaced had. -# -# The node that was replaced is queued to be freed. -# ------------------------------------------------------------------------------ -func replace_node(base_node, path_or_node, with_this): - var path = path_or_node - - if(typeof(path_or_node) != TYPE_STRING): - # This will cause an engine error if it fails. It always returns a - # NodePath, even if it fails. Checking the name count is the only way - # I found to check if it found something or not (after it worked I - # didn't look any farther). - path = base_node.get_path_to(path_or_node) - if(path.get_name_count() == 0): - _lgr.error('You passed an object that base_node does not have. Cannot replace node.') - return - - if(!base_node.has_node(path)): - _lgr.error(str('Could not find node at path [', path, ']')) - return - - var to_replace = base_node.get_node(path) - var parent = to_replace.get_parent() - var replace_name = to_replace.get_name() - - parent.remove_child(to_replace) - parent.add_child(with_this) - with_this.set_name(replace_name) - with_this.set_owner(parent) - - var groups = to_replace.get_groups() - for i in range(groups.size()): - with_this.add_to_group(groups[i]) - - to_replace.queue_free() diff --git a/addons/gut/test_collector.gd b/addons/gut/test_collector.gd deleted file mode 100644 index fac2267..0000000 --- a/addons/gut/test_collector.gd +++ /dev/null @@ -1,241 +0,0 @@ -# ------------------------------------------------------------------------------ -# Used to keep track of info about each test ran. -# ------------------------------------------------------------------------------ -class Test: - # indicator if it passed or not. defaults to true since it takes only - # one failure to make it not pass. _fail in gut will set this. - var passed = true - # the name of the function - var name = "" - # flag to know if the name has been printed yet. - var has_printed_name = false - # the line number the test is on - var line_number = -1 - -# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -class TestScript: - var inner_class_name = null - var tests = [] - var path = null - var _utils = null - var _lgr = null - - func _init(utils=null, logger=null): - _utils = utils - _lgr = logger - - func to_s(): - var to_return = path - if(inner_class_name != null): - to_return += str('.', inner_class_name) - to_return += "\n" - for i in range(tests.size()): - to_return += str(' ', tests[i].name, "\n") - return to_return - - func get_new(): - var TheScript = load(path) - var inst = null - if(inner_class_name != null): - inst = TheScript.get(inner_class_name).new() - else: - inst = TheScript.new() - return inst - - func get_full_name(): - var to_return = path - if(inner_class_name != null): - to_return += '.' + inner_class_name - return to_return - - func get_filename(): - return path.get_file() - - func has_inner_class(): - return inner_class_name != null - - func export_to(config_file, section): - config_file.set_value(section, 'path', path) - config_file.set_value(section, 'inner_class', inner_class_name) - var names = [] - for i in range(tests.size()): - names.append(tests[i].name) - config_file.set_value(section, 'tests', names) - - func _remap_path(path): - var to_return = path - if(!_utils.file_exists(path)): - _lgr.debug('Checking for remap for: ' + path) - var remap_path = path.get_basename() + '.gd.remap' - if(_utils.file_exists(remap_path)): - var cf = ConfigFile.new() - cf.load(remap_path) - to_return = cf.get_value('remap', 'path') - else: - _lgr.warn('Could not find remap file ' + remap_path) - return to_return - - func import_from(config_file, section): - path = config_file.get_value(section, 'path') - path = _remap_path(path) - var test_names = config_file.get_value(section, 'tests') - for i in range(test_names.size()): - var t = Test.new() - t.name = test_names[i] - tests.append(t) - # Null is an acceptable value, but you can't pass null as a default to - # get_value since it thinks you didn't send a default...then it spits - # out red text. This works around that. - var inner_name = config_file.get_value(section, 'inner_class', 'Placeholder') - if(inner_name != 'Placeholder'): - inner_class_name = inner_name - else: # just being explicit - inner_class_name = null - - -# ------------------------------------------------------------------------------ -# start test_collector, I don't think I like the name. -# ------------------------------------------------------------------------------ -var scripts = [] -var _test_prefix = 'test_' -var _test_class_prefix = 'Test' - -var _utils = load('res://addons/gut/utils.gd').new() -var _lgr = _utils.get_logger() - -func _parse_script(script): - var file = File.new() - var line = "" - var line_count = 0 - var inner_classes = [] - var scripts_found = [] - - file.open(script.path, 1) - while(!file.eof_reached()): - line_count += 1 - line = file.get_line() - #Add a test - if(line.begins_with("func " + _test_prefix)): - var from = line.find(_test_prefix) - var line_len = line.find("(") - from - var new_test = Test.new() - new_test.name = line.substr(from, line_len) - new_test.line_number = line_count - script.tests.append(new_test) - - if(line.begins_with('class ')): - var iclass_name = line.replace('class ', '') - iclass_name = iclass_name.replace(':', '') - if(iclass_name.begins_with(_test_class_prefix)): - inner_classes.append(iclass_name) - - scripts_found.append(script.path) - - for i in range(inner_classes.size()): - var ts = TestScript.new(_utils, _lgr) - ts.path = script.path - ts.inner_class_name = inner_classes[i] - if(_parse_inner_class_tests(ts)): - scripts.append(ts) - scripts_found.append(script.path + '[' + inner_classes[i] +']') - - file.close() - return scripts_found - -func _parse_inner_class_tests(script): - var inst = script.get_new() - - if(!inst is _utils.Test): - _lgr.warn('Ignoring ' + script.inner_class_name + ' because it starts with "' + _test_class_prefix + '" but does not extend addons/gut/test.gd') - return false - - var methods = inst.get_method_list() - for i in range(methods.size()): - var name = methods[i]['name'] - if(name.begins_with(_test_prefix) and methods[i]['flags'] == 65): - var t = Test.new() - t.name = name - script.tests.append(t) - - return true -# ----------------- -# Public -# ----------------- -func add_script(path): - # SHORTCIRCUIT - if(has_script(path)): - return [] - - var f = File.new() - # SHORTCIRCUIT - if(!f.file_exists(path)): - _lgr.error('Could not find script: ' + path) - return - - var ts = TestScript.new(_utils, _lgr) - ts.path = path - scripts.append(ts) - return _parse_script(ts) - -func to_s(): - var to_return = '' - for i in range(scripts.size()): - to_return += scripts[i].to_s() + "\n" - return to_return -func get_logger(): - return _lgr - -func set_logger(logger): - _lgr = logger - -func get_test_prefix(): - return _test_prefix - -func set_test_prefix(test_prefix): - _test_prefix = test_prefix - -func get_test_class_prefix(): - return _test_class_prefix - -func set_test_class_prefix(test_class_prefix): - _test_class_prefix = test_class_prefix - -func clear(): - scripts.clear() - -func has_script(path): - var found = false - var idx = 0 - while(idx < scripts.size() and !found): - if(scripts[idx].path == path): - found = true - else: - idx += 1 - return found - -func export_tests(path): - var success = true - var f = ConfigFile.new() - for i in range(scripts.size()): - scripts[i].export_to(f, str('TestScript-', i)) - var result = f.save(path) - if(result != OK): - _lgr.error(str('Could not save exported tests to [', path, ']. Error code: ', result)) - success = false - return success - -func import_tests(path): - var success = false - var f = ConfigFile.new() - var result = f.load(path) - if(result != OK): - _lgr.error(str('Could not load exported tests from [', path, ']. Error code: ', result)) - else: - var sections = f.get_sections() - for key in sections: - var ts = TestScript.new(_utils, _lgr) - ts.import_from(f, key) - scripts.append(ts) - success = true - return success diff --git a/addons/gut/thing_counter.gd b/addons/gut/thing_counter.gd deleted file mode 100644 index a9b0b48..0000000 --- a/addons/gut/thing_counter.gd +++ /dev/null @@ -1,43 +0,0 @@ -var things = {} - -func get_unique_count(): - return things.size() - -func add(thing): - if(things.has(thing)): - things[thing] += 1 - else: - things[thing] = 1 - -func has(thing): - return things.has(thing) - -func get(thing): - var to_return = 0 - if(things.has(thing)): - to_return = things[thing] - return to_return - -func sum(): - var count = 0 - for key in things: - count += things[key] - return count - -func to_s(): - var to_return = "" - for key in things: - to_return += str(key, ": ", things[key], "\n") - to_return += str("sum: ", sum()) - return to_return - -func get_max_count(): - var max_val = null - for key in things: - if(max_val == null or things[key] > max_val): - max_val = things[key] - return max_val - -func add_array_items(array): - for i in range(array.size()): - add(array[i]) diff --git a/addons/gut/utils.gd b/addons/gut/utils.gd deleted file mode 100644 index a7626b8..0000000 --- a/addons/gut/utils.gd +++ /dev/null @@ -1,122 +0,0 @@ -var _Logger = load('res://addons/gut/logger.gd') # everything should use get_logger - -var Doubler = load('res://addons/gut/doubler.gd') -var HookScript = load('res://addons/gut/hook_script.gd') -var MethodMaker = load('res://addons/gut/method_maker.gd') -var Spy = load('res://addons/gut/spy.gd') -var Stubber = load('res://addons/gut/stubber.gd') -var StubParams = load('res://addons/gut/stub_params.gd') -var Summary = load('res://addons/gut/summary.gd') -var Test = load('res://addons/gut/test.gd') -var TestCollector = load('res://addons/gut/test_collector.gd') -var ThingCounter = load('res://addons/gut/thing_counter.gd') -var OneToMany = load('res://addons/gut/one_to_many.gd') - -const GUT_METADATA = '__gut_metadata_' - -enum DOUBLE_STRATEGY{ - FULL, - PARTIAL -} - -var _file_checker = File.new() - -func is_version_30(): - var info = Engine.get_version_info() - return info.major == 3 and info.minor == 0 - -func is_version_31(): - var info = Engine.get_version_info() - return info.major == 3 and info.minor == 1 - -# ------------------------------------------------------------------------------ -# Everything should get a logger through this. -# -# Eventually I want to make this get a single instance of a logger but I'm not -# sure how to do that without everything having to be in the tree which I -# DO NOT want to to do. I'm thinking of writings some instance ids to a file -# and loading them in the _init for this. -# ------------------------------------------------------------------------------ -func get_logger(): - return _Logger.new() - -# ------------------------------------------------------------------------------ -# Returns an array created by splitting the string by the delimiter -# ------------------------------------------------------------------------------ -func split_string(to_split, delim): - var to_return = [] - - var loc = to_split.find(delim) - while(loc != -1): - to_return.append(to_split.substr(0, loc)) - to_split = to_split.substr(loc + 1, to_split.length() - loc) - loc = to_split.find(delim) - to_return.append(to_split) - return to_return - -# ------------------------------------------------------------------------------ -# Returns a string containing all the elements in the array separated by delim -# ------------------------------------------------------------------------------ -func join_array(a, delim): - var to_return = '' - for i in range(a.size()): - to_return += str(a[i]) - if(i != a.size() -1): - to_return += str(delim) - return to_return - -# ------------------------------------------------------------------------------ -# return if_null if value is null otherwise return value -# ------------------------------------------------------------------------------ -func nvl(value, if_null): - if(value == null): - return if_null - else: - return value - -# ------------------------------------------------------------------------------ -# returns true if the object has been freed, false if not -# -# From what i've read, the weakref approach should work. It seems to work most -# of the time but sometimes it does not catch it. The str comparison seems to -# fill in the gaps. I've not seen any errors after adding that check. -# ------------------------------------------------------------------------------ -func is_freed(obj): - var wr = weakref(obj) - return !(wr.get_ref() and str(obj) != '[Deleted Object]') - -func is_not_freed(obj): - return !is_freed(obj) - -func is_double(obj): - return obj.get(GUT_METADATA) != null - -func extract_property_from_array(source, property): - var to_return = [] - for i in (source.size()): - to_return.append(source[i].get(property)) - return to_return - -func file_exists(path): - return _file_checker.file_exists(path) - -func write_file(path, content): - var f = File.new() - f.open(path, f.WRITE) - f.store_string(content) - f.close() - -func is_null_or_empty(text): - return text == null or text == '' - -func get_native_class_name(thing): - var to_return = null - if(is_native_class(thing)): - to_return = thing.new().get_class() - return to_return - -func is_native_class(thing): - var it_is = false - if(typeof(thing) == TYPE_OBJECT): - it_is = str(thing).begins_with("[GDScriptNativeClass:") - return it_is diff --git a/justfile b/justfile index 945ff70..0fd02d3 100644 --- a/justfile +++ b/justfile @@ -5,9 +5,6 @@ itchio := "damantisshrimp/taqin" edit: godot --editor --quiet & -test: - godot --debug --script {{src_dir}}/addons/gut/gut_cmdln.gd - export-android: mkdir -p {{build_dir}}/android godot --export "Android" {{build_dir}}/android diff --git a/project.godot b/project.godot index c67b1b0..a43c645 100644 --- a/project.godot +++ b/project.godot @@ -40,10 +40,6 @@ window/size/height.mobile=1200 window/stretch/mode.mobile="2d" window/stretch/aspect.mobile="expand" -[editor_plugins] - -enabled=PoolStringArray( "gut" ) - [input_devices] pointing/emulate_touch_from_mouse=true diff --git a/test/tests.tscn b/test/tests.tscn deleted file mode 100644 index 954778f..0000000 --- a/test/tests.tscn +++ /dev/null @@ -1,38 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://addons/gut/gut.gd" type="Script" id=1] - -[node name="Gut" type="Control"] -self_modulate = Color( 1, 1, 1, 0 ) -anchor_right = 1.0 -anchor_bottom = 1.0 -rect_min_size = Vector2( 740, 250 ) -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} -_select_script = "" -_tests_like = "" -_inner_class_name = "" -_run_on_load = false -_should_maximize = false -_should_print_to_console = true -_log_level = 1 -_yield_between_tests = false -_disable_strict_datatype_checks = false -_test_prefix = "test_" -_file_prefix = "test_" -_file_extension = ".gd" -_inner_class_prefix = "Test" -_temp_directory = "user://gut_temp_directory" -_export_path = "" -_include_subdirectories = false -_directory1 = "res://test/unit" -_directory2 = "" -_directory3 = "" -_directory4 = "" -_directory5 = "" -_directory6 = "" -_double_strategy = 1 -_pre_run_script = "" -_post_run_script = "" diff --git a/test/unit/test_example.gd b/test/unit/test_example.gd deleted file mode 100644 index e9ff6f5..0000000 --- a/test/unit/test_example.gd +++ /dev/null @@ -1,27 +0,0 @@ -extends "res://addons/gut/test.gd" -func before_each(): - gut.p("ran setup", 2) - -func after_each(): - gut.p("ran teardown", 2) - -func before_all(): - gut.p("ran run setup", 2) - -func after_all(): - gut.p("ran run teardown", 2) - -func test_assert_eq_number_not_equal(): - assert_eq(1, 2, "Should fail. 1 != 2") - -func test_assert_eq_number_equal(): - assert_eq('asdf', 'asdf', "Should pass") - -func test_assert_true_with_true(): - assert_true(true, "Should pass, true is true") - -func test_assert_true_with_false(): - assert_true(false, "Should fail") - -func test_something_else(): - assert_true(false, "didn't work")