mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-22 09:40:25 +01:00
Update GUT to version 9.1.1
This commit is contained in:
parent
b18726eac9
commit
cea41d6567
65 changed files with 4178 additions and 2995 deletions
|
@ -1,146 +1,12 @@
|
||||||
extends Node2D
|
extends Node2D
|
||||||
|
# ##############################################################################
|
||||||
|
# This is a wrapper around the normal and compact gui controls and serves as
|
||||||
|
# the interface between gut.gd and the gui. The GutRunner creates an instance
|
||||||
|
# of this and then this takes care of managing the different GUI controls.
|
||||||
|
# ##############################################################################
|
||||||
|
@onready var _normal_gui = $Normal
|
||||||
|
@onready var _compact_gui = $Compact
|
||||||
|
|
||||||
|
|
||||||
class GuiHandler:
|
|
||||||
var _gui = null
|
|
||||||
var _gut = null
|
|
||||||
|
|
||||||
var _ctrls = {
|
|
||||||
btn_continue = null,
|
|
||||||
path_dir = null,
|
|
||||||
path_file = null,
|
|
||||||
prog_script = null,
|
|
||||||
prog_test = null,
|
|
||||||
rtl = null,
|
|
||||||
rtl_bg = null,
|
|
||||||
time_label = null
|
|
||||||
}
|
|
||||||
|
|
||||||
func _init(gui):
|
|
||||||
_gui = gui
|
|
||||||
|
|
||||||
# Brute force, but flexible.
|
|
||||||
_ctrls.btn_continue = _get_first_child_named("Continue", _gui)
|
|
||||||
_ctrls.path_dir = _get_first_child_named("Path", _gui)
|
|
||||||
_ctrls.path_file = _get_first_child_named("File", _gui)
|
|
||||||
_ctrls.prog_script = _get_first_child_named("ProgressScript", _gui)
|
|
||||||
_ctrls.prog_test = _get_first_child_named("ProgressTest", _gui)
|
|
||||||
_ctrls.rtl = _get_first_child_named("Output", _gui)
|
|
||||||
_ctrls.rtl_bg = _get_first_child_named("OutputBG", _gui)
|
|
||||||
_ctrls.time_label = _get_first_child_named("TimeLabel", _gui)
|
|
||||||
|
|
||||||
_ctrls.btn_continue.visible = false
|
|
||||||
_ctrls.btn_continue.pressed.connect(_on_continue_pressed)
|
|
||||||
|
|
||||||
_ctrls.prog_script.value = 0
|
|
||||||
_ctrls.prog_test.value = 0
|
|
||||||
_ctrls.path_dir.text = ""
|
|
||||||
_ctrls.path_file.text = ""
|
|
||||||
_ctrls.time_label.text = ""
|
|
||||||
|
|
||||||
# ------------------
|
|
||||||
# Events
|
|
||||||
# ------------------
|
|
||||||
func _on_continue_pressed():
|
|
||||||
_ctrls.btn_continue.visible = false
|
|
||||||
_gut.end_teardown_pause()
|
|
||||||
|
|
||||||
func _on_gut_start_run():
|
|
||||||
if _ctrls.rtl != null:
|
|
||||||
_ctrls.rtl.clear()
|
|
||||||
set_num_scripts(_gut.get_test_collector().scripts.size())
|
|
||||||
|
|
||||||
func _on_gut_end_run():
|
|
||||||
_ctrls.time_label.text = ""
|
|
||||||
|
|
||||||
func _on_gut_start_script(script_obj):
|
|
||||||
next_script(script_obj.get_full_name(), script_obj.tests.size())
|
|
||||||
|
|
||||||
func _on_gut_end_script():
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _on_gut_start_test(test_name):
|
|
||||||
next_test(test_name)
|
|
||||||
|
|
||||||
func _on_gut_end_test():
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _on_gut_start_pause():
|
|
||||||
pause_before_teardown()
|
|
||||||
|
|
||||||
func _on_gut_end_pause():
|
|
||||||
pass
|
|
||||||
|
|
||||||
# ------------------
|
|
||||||
# Private
|
|
||||||
# ------------------
|
|
||||||
func _get_first_child_named(obj_name, parent_obj):
|
|
||||||
if parent_obj == null:
|
|
||||||
return null
|
|
||||||
|
|
||||||
var kids = parent_obj.get_children()
|
|
||||||
var index = 0
|
|
||||||
var to_return = null
|
|
||||||
|
|
||||||
while index < kids.size() and to_return == null:
|
|
||||||
if str(kids[index]).find(str(obj_name, ":")) != -1:
|
|
||||||
to_return = kids[index]
|
|
||||||
else:
|
|
||||||
to_return = _get_first_child_named(obj_name, kids[index])
|
|
||||||
if to_return == null:
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
# ------------------
|
|
||||||
# Public
|
|
||||||
# ------------------
|
|
||||||
func set_num_scripts(val):
|
|
||||||
_ctrls.prog_script.value = 0
|
|
||||||
_ctrls.prog_script.max_value = val
|
|
||||||
|
|
||||||
func next_script(path, num_tests):
|
|
||||||
_ctrls.prog_script.value += 1
|
|
||||||
_ctrls.prog_test.value = 0
|
|
||||||
_ctrls.prog_test.max_value = num_tests
|
|
||||||
|
|
||||||
_ctrls.path_dir.text = path.get_base_dir()
|
|
||||||
_ctrls.path_file.text = path.get_file()
|
|
||||||
|
|
||||||
func next_test(test_name):
|
|
||||||
_ctrls.prog_test.value += 1
|
|
||||||
|
|
||||||
func pause_before_teardown():
|
|
||||||
_ctrls.btn_continue.visible = true
|
|
||||||
|
|
||||||
func set_gut(g):
|
|
||||||
_gut = g
|
|
||||||
g.start_run.connect(_on_gut_start_run)
|
|
||||||
g.end_run.connect(_on_gut_end_run)
|
|
||||||
|
|
||||||
g.start_script.connect(_on_gut_start_script)
|
|
||||||
g.end_script.connect(_on_gut_end_script)
|
|
||||||
|
|
||||||
g.start_test.connect(_on_gut_start_test)
|
|
||||||
g.end_test.connect(_on_gut_end_test)
|
|
||||||
|
|
||||||
g.start_pause_before_teardown.connect(_on_gut_start_pause)
|
|
||||||
g.end_pause_before_teardown.connect(_on_gut_end_pause)
|
|
||||||
|
|
||||||
func get_textbox():
|
|
||||||
return _ctrls.rtl
|
|
||||||
|
|
||||||
func set_elapsed_time(t):
|
|
||||||
_ctrls.time_label.text = str(t, "s")
|
|
||||||
|
|
||||||
func set_bg_color(c):
|
|
||||||
_ctrls.rtl_bg.color = c
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
var _large_handler = null
|
|
||||||
var _min_handler = null
|
|
||||||
var gut = null:
|
var gut = null:
|
||||||
set(val):
|
set(val):
|
||||||
gut = val
|
gut = val
|
||||||
|
@ -148,54 +14,92 @@ var gut = null:
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
_large_handler = GuiHandler.new($Large)
|
_normal_gui.switch_modes.connect(use_compact_mode.bind(true))
|
||||||
_min_handler = GuiHandler.new($Min)
|
_compact_gui.switch_modes.connect(use_compact_mode.bind(false))
|
||||||
|
|
||||||
$Min.visible = false
|
_normal_gui.set_title("GUT")
|
||||||
$Large.visible = !$Min.visible
|
_compact_gui.set_title("GUT")
|
||||||
|
|
||||||
|
_normal_gui.align_right()
|
||||||
|
_compact_gui.to_bottom_right()
|
||||||
|
|
||||||
|
use_compact_mode(false)
|
||||||
|
|
||||||
|
if get_parent() == get_tree().root:
|
||||||
|
_test_running_setup()
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta):
|
func _test_running_setup():
|
||||||
if gut != null and gut.is_running():
|
set_font_size(100)
|
||||||
_large_handler.set_elapsed_time(gut.get_elapsed_time())
|
_normal_gui.get_textbox().text = "hello world, how are you doing?"
|
||||||
_min_handler.set_elapsed_time(gut.get_elapsed_time())
|
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------
|
||||||
|
# Private
|
||||||
|
# ------------------------
|
||||||
func _set_gut(val):
|
func _set_gut(val):
|
||||||
_large_handler.set_gut(val)
|
if _normal_gui.get_gut() == val:
|
||||||
_min_handler.set_gut(val)
|
return
|
||||||
|
_normal_gui.set_gut(val)
|
||||||
|
_compact_gui.set_gut(val)
|
||||||
|
|
||||||
|
val.start_run.connect(_on_gut_start_run)
|
||||||
|
val.end_run.connect(_on_gut_end_run)
|
||||||
|
val.start_pause_before_teardown.connect(_on_gut_pause)
|
||||||
|
val.end_pause_before_teardown.connect(_on_pause_end)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_both_titles(text):
|
||||||
|
_normal_gui.set_title(text)
|
||||||
|
_compact_gui.set_title(text)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------
|
||||||
|
# Events
|
||||||
|
# ------------------------
|
||||||
|
func _on_gut_start_run():
|
||||||
|
_set_both_titles("Running")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_end_run():
|
||||||
|
_set_both_titles("Finished")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_pause():
|
||||||
|
_set_both_titles("-- Paused --")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_pause_end():
|
||||||
|
_set_both_titles("Running")
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------
|
||||||
|
# Public
|
||||||
|
# ------------------------
|
||||||
func get_textbox():
|
func get_textbox():
|
||||||
return _large_handler.get_textbox()
|
return _normal_gui.get_textbox()
|
||||||
|
|
||||||
|
|
||||||
func set_font_size(new_size):
|
func set_font_size(new_size):
|
||||||
var rtl = _large_handler.get_textbox()
|
var rtl = _normal_gui.get_textbox()
|
||||||
if rtl.get("custom_fonts/normal_font") != null:
|
|
||||||
rtl.get("custom_fonts/bold_italics_font").size = new_size
|
rtl.set("theme_override_font_sizes/bold_italics_font_size", new_size)
|
||||||
rtl.get("custom_fonts/bold_font").size = new_size
|
rtl.set("theme_override_font_sizes/bold_font_size", new_size)
|
||||||
rtl.get("custom_fonts/italics_font").size = new_size
|
rtl.set("theme_override_font_sizes/italics_font_size", new_size)
|
||||||
rtl.get("custom_fonts/normal_font").size = new_size
|
rtl.set("theme_override_font_sizes/normal_font_size", new_size)
|
||||||
|
|
||||||
|
|
||||||
func set_font(font_name):
|
func set_font(font_name):
|
||||||
pass
|
_set_all_fonts_in_rtl(_normal_gui.get_textbox(), font_name)
|
||||||
#_set_all_fonts_in_rtl(_large_handler.get_textbox(), font_name)
|
|
||||||
|
|
||||||
|
|
||||||
# Needs rework for 4.0, DynamicFont DNE
|
|
||||||
func _set_font(rtl, font_name, custom_name):
|
func _set_font(rtl, font_name, custom_name):
|
||||||
pass
|
if font_name == null:
|
||||||
# if(font_name == null):
|
rtl.add_theme_font_override(custom_name, null)
|
||||||
# rtl.set('custom_fonts/' + custom_name, null)
|
else:
|
||||||
# else:
|
var dyn_font = FontFile.new()
|
||||||
# var dyn_font = DynamicFont.new()
|
dyn_font.load_dynamic_font("res://addons/gut/fonts/" + font_name + ".ttf")
|
||||||
# var font_data = DynamicFontData.new()
|
rtl.add_theme_font_override(custom_name, dyn_font)
|
||||||
# font_data.font_path = 'res://addons/gut/fonts/' + font_name + '.ttf'
|
|
||||||
# font_data.antialiased = true
|
|
||||||
# dyn_font.font_data = font_data
|
|
||||||
# rtl.set('custom_fonts/' + custom_name, dyn_font)
|
|
||||||
|
|
||||||
|
|
||||||
func _set_all_fonts_in_rtl(rtl, base_name):
|
func _set_all_fonts_in_rtl(rtl, base_name):
|
||||||
|
@ -212,8 +116,18 @@ func _set_all_fonts_in_rtl(rtl, base_name):
|
||||||
|
|
||||||
|
|
||||||
func set_default_font_color(color):
|
func set_default_font_color(color):
|
||||||
_large_handler.get_textbox().set("custom_colors/default_color", color)
|
_normal_gui.get_textbox().set("custom_colors/default_color", color)
|
||||||
|
|
||||||
|
|
||||||
func set_background_color(color):
|
func set_background_color(color):
|
||||||
_large_handler.set_bg_color(color)
|
_normal_gui.set_bg_color(color)
|
||||||
|
|
||||||
|
|
||||||
|
func use_compact_mode(should = true):
|
||||||
|
_compact_gui.visible = should
|
||||||
|
_normal_gui.visible = !should
|
||||||
|
|
||||||
|
|
||||||
|
func set_opacity(val):
|
||||||
|
_normal_gui.modulate.a = val
|
||||||
|
_compact_gui.modulate.a = val
|
||||||
|
|
|
@ -1,375 +1,16 @@
|
||||||
[gd_scene load_steps=4 format=3 uid="uid://m28heqtswbuq"]
|
[gd_scene load_steps=4 format=3 uid="uid://m28heqtswbuq"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/gut/GutScene.gd" id="1_b4m8y"]
|
[ext_resource type="Script" path="res://addons/gut/GutScene.gd" id="1_b4m8y"]
|
||||||
[ext_resource type="Theme" uid="uid://cstkhwkpajvqu" path="res://addons/gut/gui/GutSceneTheme.tres" id="1_s37wl"]
|
[ext_resource type="PackedScene" uid="uid://duxblir3vu8x7" path="res://addons/gut/gui/NormalGui.tscn" id="2_j6ywb"]
|
||||||
[ext_resource type="FontFile" uid="uid://bnh0lslf4yh87" path="res://addons/gut/fonts/CourierPrime-Regular.ttf" id="3_qvb8f"]
|
[ext_resource type="PackedScene" uid="uid://cnqqdfsn80ise" path="res://addons/gut/gui/MinGui.tscn" id="3_3glw1"]
|
||||||
|
|
||||||
[node name="GutScene" type="Node2D"]
|
[node name="GutScene" type="Node2D"]
|
||||||
script = ExtResource("1_b4m8y")
|
script = ExtResource("1_b4m8y")
|
||||||
|
|
||||||
[node name="Large" type="Panel" parent="."]
|
[node name="Normal" parent="." instance=ExtResource("2_j6ywb")]
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 388.0
|
|
||||||
theme = ExtResource("1_s37wl")
|
|
||||||
|
|
||||||
[node name="MainBox" type="VBoxContainer" parent="Large"]
|
[node name="Compact" parent="." instance=ExtResource("3_3glw1")]
|
||||||
anchors_preset = 15
|
offset_left = 5.0
|
||||||
anchor_right = 1.0
|
offset_top = 273.0
|
||||||
anchor_bottom = 1.0
|
offset_right = 265.0
|
||||||
grow_horizontal = 2
|
offset_bottom = 403.0
|
||||||
grow_vertical = 2
|
|
||||||
metadata/_edit_layout_mode = 1
|
|
||||||
|
|
||||||
[node name="TitleBar" type="Panel" parent="Large/MainBox"]
|
|
||||||
custom_minimum_size = Vector2(0, 25)
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
|
|
||||||
[node name="TitleBox" type="HBoxContainer" parent="Large/MainBox/TitleBar"]
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
offset_top = 2.0
|
|
||||||
offset_bottom = 3.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
metadata/_edit_layout_mode = 1
|
|
||||||
|
|
||||||
[node name="Spacer1" type="CenterContainer" parent="Large/MainBox/TitleBar/TitleBox"]
|
|
||||||
offset_right = 285.0
|
|
||||||
offset_bottom = 26.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="Large/MainBox/TitleBar/TitleBox"]
|
|
||||||
offset_left = 289.0
|
|
||||||
offset_top = 3.0
|
|
||||||
offset_right = 334.0
|
|
||||||
offset_bottom = 23.0
|
|
||||||
text = "Title"
|
|
||||||
|
|
||||||
[node name="Spacer2" type="CenterContainer" parent="Large/MainBox/TitleBar/TitleBox"]
|
|
||||||
offset_left = 338.0
|
|
||||||
offset_right = 623.0
|
|
||||||
offset_bottom = 26.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="TimeLabel" type="Label" parent="Large/MainBox/TitleBar/TitleBox"]
|
|
||||||
custom_minimum_size = Vector2(90, 0)
|
|
||||||
offset_left = 627.0
|
|
||||||
offset_top = 3.0
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 23.0
|
|
||||||
text = "999.999s"
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Large/MainBox"]
|
|
||||||
offset_top = 29.0
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 379.0
|
|
||||||
size_flags_vertical = 3
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="Large/MainBox/HBoxContainer"]
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 350.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="OutputBG" type="ColorRect" parent="Large/MainBox/HBoxContainer/VBoxContainer"]
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 300.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
size_flags_vertical = 3
|
|
||||||
color = Color(0.0745098, 0.0705882, 0.0784314, 1)
|
|
||||||
metadata/_edit_layout_mode = 1
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/OutputBG"]
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
|
|
||||||
[node name="S2" type="CenterContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_right = 5.0
|
|
||||||
offset_bottom = 300.0
|
|
||||||
|
|
||||||
[node name="Output" type="RichTextLabel" parent="Large/MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
|
||||||
offset_left = 9.0
|
|
||||||
offset_right = 708.0
|
|
||||||
offset_bottom = 300.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
focus_mode = 2
|
|
||||||
bbcode_enabled = true
|
|
||||||
scroll_following = true
|
|
||||||
selection_enabled = true
|
|
||||||
|
|
||||||
[node name="S1" type="CenterContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_left = 712.0
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 300.0
|
|
||||||
|
|
||||||
[node name="ControlBox" type="HBoxContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer"]
|
|
||||||
offset_top = 304.0
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 350.0
|
|
||||||
|
|
||||||
[node name="S1" type="CenterContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_right = 5.0
|
|
||||||
offset_bottom = 46.0
|
|
||||||
|
|
||||||
[node name="ProgressBars" type="VBoxContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
|
||||||
offset_left = 9.0
|
|
||||||
offset_right = 176.0
|
|
||||||
offset_bottom = 46.0
|
|
||||||
|
|
||||||
[node name="TestBox" type="HBoxContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars"]
|
|
||||||
offset_right = 167.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/TestBox"]
|
|
||||||
custom_minimum_size = Vector2(60, 0)
|
|
||||||
offset_right = 60.0
|
|
||||||
offset_bottom = 20.0
|
|
||||||
text = "Tests"
|
|
||||||
|
|
||||||
[node name="ProgressTest" type="ProgressBar" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/TestBox"]
|
|
||||||
custom_minimum_size = Vector2(100, 0)
|
|
||||||
offset_left = 64.0
|
|
||||||
offset_right = 164.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
value = 25.0
|
|
||||||
|
|
||||||
[node name="ScriptBox" type="HBoxContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars"]
|
|
||||||
offset_top = 25.0
|
|
||||||
offset_right = 167.0
|
|
||||||
offset_bottom = 46.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/ScriptBox"]
|
|
||||||
custom_minimum_size = Vector2(60, 0)
|
|
||||||
offset_right = 63.0
|
|
||||||
offset_bottom = 20.0
|
|
||||||
text = "Scripts"
|
|
||||||
|
|
||||||
[node name="ProgressScript" type="ProgressBar" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/ScriptBox"]
|
|
||||||
custom_minimum_size = Vector2(100, 0)
|
|
||||||
offset_left = 67.0
|
|
||||||
offset_right = 167.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
value = 75.0
|
|
||||||
|
|
||||||
[node name="PathDisplay" type="VBoxContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
|
||||||
offset_left = 180.0
|
|
||||||
offset_right = 385.0
|
|
||||||
offset_bottom = 46.0
|
|
||||||
size_flags_vertical = 3
|
|
||||||
|
|
||||||
[node name="Path" type="Label" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay"]
|
|
||||||
offset_right = 205.0
|
|
||||||
offset_bottom = 16.0
|
|
||||||
theme_override_fonts/font = ExtResource("3_qvb8f")
|
|
||||||
theme_override_font_sizes/font_size = 11
|
|
||||||
text = "res://test/integration/whatever"
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay"]
|
|
||||||
offset_top = 20.0
|
|
||||||
offset_right = 205.0
|
|
||||||
offset_bottom = 36.0
|
|
||||||
|
|
||||||
[node name="S3" type="CenterContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay/HBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_right = 5.0
|
|
||||||
offset_bottom = 16.0
|
|
||||||
|
|
||||||
[node name="File" type="Label" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay/HBoxContainer"]
|
|
||||||
offset_left = 9.0
|
|
||||||
offset_right = 128.0
|
|
||||||
offset_bottom = 16.0
|
|
||||||
theme_override_fonts/font = ExtResource("3_qvb8f")
|
|
||||||
theme_override_font_sizes/font_size = 11
|
|
||||||
text = "test_this_thing.gd"
|
|
||||||
|
|
||||||
[node name="Spacer1" type="CenterContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
|
||||||
offset_left = 389.0
|
|
||||||
offset_right = 624.0
|
|
||||||
offset_bottom = 46.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="Continue" type="Button" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
|
||||||
offset_left = 628.0
|
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 708.0
|
|
||||||
offset_bottom = 35.0
|
|
||||||
size_flags_vertical = 4
|
|
||||||
text = "Continue
|
|
||||||
"
|
|
||||||
|
|
||||||
[node name="S3" type="CenterContainer" parent="Large/MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_left = 712.0
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 46.0
|
|
||||||
|
|
||||||
[node name="BottomPad" type="CenterContainer" parent="Large/MainBox"]
|
|
||||||
custom_minimum_size = Vector2(0, 5)
|
|
||||||
offset_top = 383.0
|
|
||||||
offset_right = 717.0
|
|
||||||
offset_bottom = 388.0
|
|
||||||
|
|
||||||
[node name="Min" type="Panel" parent="."]
|
|
||||||
clip_contents = true
|
|
||||||
offset_left = 456.0
|
|
||||||
offset_top = 396.0
|
|
||||||
offset_right = 719.0
|
|
||||||
offset_bottom = 515.0
|
|
||||||
theme = ExtResource("1_s37wl")
|
|
||||||
metadata/_edit_group_ = true
|
|
||||||
|
|
||||||
[node name="MainBox" type="VBoxContainer" parent="Min"]
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
metadata/_edit_layout_mode = 1
|
|
||||||
|
|
||||||
[node name="TitleBar" type="Panel" parent="Min/MainBox"]
|
|
||||||
custom_minimum_size = Vector2(0, 25)
|
|
||||||
offset_right = 266.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
|
|
||||||
[node name="TitleBox" type="HBoxContainer" parent="Min/MainBox/TitleBar"]
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
offset_top = 2.0
|
|
||||||
offset_bottom = 3.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
metadata/_edit_layout_mode = 1
|
|
||||||
|
|
||||||
[node name="Spacer1" type="CenterContainer" parent="Min/MainBox/TitleBar/TitleBox"]
|
|
||||||
offset_right = 77.0
|
|
||||||
offset_bottom = 26.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="Min/MainBox/TitleBar/TitleBox"]
|
|
||||||
offset_left = 81.0
|
|
||||||
offset_top = 3.0
|
|
||||||
offset_right = 126.0
|
|
||||||
offset_bottom = 23.0
|
|
||||||
text = "Title"
|
|
||||||
|
|
||||||
[node name="Spacer2" type="CenterContainer" parent="Min/MainBox/TitleBar/TitleBox"]
|
|
||||||
offset_left = 130.0
|
|
||||||
offset_right = 208.0
|
|
||||||
offset_bottom = 26.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="TimeLabel" type="Label" parent="Min/MainBox/TitleBar/TitleBox"]
|
|
||||||
offset_left = 212.0
|
|
||||||
offset_top = 3.0
|
|
||||||
offset_right = 266.0
|
|
||||||
offset_bottom = 23.0
|
|
||||||
text = "0.000s"
|
|
||||||
|
|
||||||
[node name="Body" type="HBoxContainer" parent="Min/MainBox"]
|
|
||||||
offset_top = 29.0
|
|
||||||
offset_right = 266.0
|
|
||||||
offset_bottom = 119.0
|
|
||||||
size_flags_vertical = 3
|
|
||||||
|
|
||||||
[node name="LeftMargin" type="CenterContainer" parent="Min/MainBox/Body"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_right = 5.0
|
|
||||||
offset_bottom = 90.0
|
|
||||||
|
|
||||||
[node name="BodyRows" type="VBoxContainer" parent="Min/MainBox/Body"]
|
|
||||||
offset_left = 9.0
|
|
||||||
offset_right = 257.0
|
|
||||||
offset_bottom = 90.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="ProgressBars" type="HBoxContainer" parent="Min/MainBox/Body/BodyRows"]
|
|
||||||
offset_right = 248.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Min/MainBox/Body/BodyRows/ProgressBars"]
|
|
||||||
offset_right = 122.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Min/MainBox/Body/BodyRows/ProgressBars/HBoxContainer"]
|
|
||||||
offset_right = 18.0
|
|
||||||
offset_bottom = 20.0
|
|
||||||
text = "T:"
|
|
||||||
|
|
||||||
[node name="ProgressTest" type="ProgressBar" parent="Min/MainBox/Body/BodyRows/ProgressBars/HBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(100, 0)
|
|
||||||
offset_left = 22.0
|
|
||||||
offset_right = 122.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
value = 25.0
|
|
||||||
|
|
||||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Min/MainBox/Body/BodyRows/ProgressBars"]
|
|
||||||
offset_left = 126.0
|
|
||||||
offset_right = 248.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Min/MainBox/Body/BodyRows/ProgressBars/HBoxContainer2"]
|
|
||||||
offset_right = 18.0
|
|
||||||
offset_bottom = 20.0
|
|
||||||
text = "S:"
|
|
||||||
|
|
||||||
[node name="ProgressScript" type="ProgressBar" parent="Min/MainBox/Body/BodyRows/ProgressBars/HBoxContainer2"]
|
|
||||||
custom_minimum_size = Vector2(100, 0)
|
|
||||||
offset_left = 22.0
|
|
||||||
offset_right = 122.0
|
|
||||||
offset_bottom = 21.0
|
|
||||||
value = 75.0
|
|
||||||
|
|
||||||
[node name="PathDisplay" type="VBoxContainer" parent="Min/MainBox/Body/BodyRows"]
|
|
||||||
offset_top = 25.0
|
|
||||||
offset_right = 248.0
|
|
||||||
offset_bottom = 61.0
|
|
||||||
size_flags_vertical = 3
|
|
||||||
|
|
||||||
[node name="Path" type="Label" parent="Min/MainBox/Body/BodyRows/PathDisplay"]
|
|
||||||
offset_right = 248.0
|
|
||||||
offset_bottom = 16.0
|
|
||||||
theme_override_fonts/font = ExtResource("3_qvb8f")
|
|
||||||
theme_override_font_sizes/font_size = 11
|
|
||||||
text = "res://test/integration/whatever"
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Min/MainBox/Body/BodyRows/PathDisplay"]
|
|
||||||
offset_top = 20.0
|
|
||||||
offset_right = 248.0
|
|
||||||
offset_bottom = 36.0
|
|
||||||
|
|
||||||
[node name="S3" type="CenterContainer" parent="Min/MainBox/Body/BodyRows/PathDisplay/HBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_right = 5.0
|
|
||||||
offset_bottom = 16.0
|
|
||||||
|
|
||||||
[node name="File" type="Label" parent="Min/MainBox/Body/BodyRows/PathDisplay/HBoxContainer"]
|
|
||||||
offset_left = 9.0
|
|
||||||
offset_right = 128.0
|
|
||||||
offset_bottom = 16.0
|
|
||||||
theme_override_fonts/font = ExtResource("3_qvb8f")
|
|
||||||
theme_override_font_sizes/font_size = 11
|
|
||||||
text = "test_this_thing.gd"
|
|
||||||
|
|
||||||
[node name="Continue" type="Button" parent="Min/MainBox/Body/BodyRows"]
|
|
||||||
offset_top = 65.0
|
|
||||||
offset_right = 248.0
|
|
||||||
offset_bottom = 90.0
|
|
||||||
text = "Continue
|
|
||||||
"
|
|
||||||
|
|
||||||
[node name="RightMargin" type="CenterContainer" parent="Min/MainBox/Body"]
|
|
||||||
custom_minimum_size = Vector2(5, 0)
|
|
||||||
offset_left = 261.0
|
|
||||||
offset_right = 266.0
|
|
||||||
offset_bottom = 90.0
|
|
||||||
|
|
|
@ -55,7 +55,8 @@ func _on_End_pressed():
|
||||||
|
|
||||||
|
|
||||||
func _on_Copy_pressed():
|
func _on_Copy_pressed():
|
||||||
OS.clipboard = rtl.text
|
return
|
||||||
|
# OS.clipboard = rtl.text
|
||||||
|
|
||||||
|
|
||||||
func _on_file_dialog_visibility_changed():
|
func _on_file_dialog_visibility_changed():
|
||||||
|
|
|
@ -24,6 +24,9 @@ func _physics_process(delta):
|
||||||
|
|
||||||
|
|
||||||
func _end_wait():
|
func _end_wait():
|
||||||
|
if _signal_to_wait_on != null and _signal_to_wait_on.is_connected(_signal_callback):
|
||||||
|
_signal_to_wait_on.disconnect(_signal_callback)
|
||||||
|
|
||||||
_wait_time = 0.0
|
_wait_time = 0.0
|
||||||
_wait_frames = 0
|
_wait_frames = 0
|
||||||
_signal_to_wait_on = null
|
_signal_to_wait_on = null
|
||||||
|
|
214
addons/gut/collected_script.gd
Normal file
214
addons/gut/collected_script.gd
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# This holds all the meta information for a test script. It contains the
|
||||||
|
# name of the inner class and an array of CollectedTests. This does not parse
|
||||||
|
# anything, it just holds the data about parsed scripts and tests. The
|
||||||
|
# TestCollector is responsible for populating this object.
|
||||||
|
#
|
||||||
|
# This class also facilitates all the exporting and importing of tests.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
var CollectedTest = load("res://addons/gut/collected_test.gd")
|
||||||
|
|
||||||
|
var _utils = null
|
||||||
|
var _lgr = null
|
||||||
|
|
||||||
|
# One entry per test found in the script. Added externally by TestCollector
|
||||||
|
var tests = []
|
||||||
|
# One entry for before_all and after_all (maybe add before_each and after_each).
|
||||||
|
# These are added by Gut when running before_all and after_all for the script.
|
||||||
|
var setup_teardown_tests = []
|
||||||
|
var inner_class_name: StringName
|
||||||
|
var path: String
|
||||||
|
|
||||||
|
# Set externally by test_collector after it can verify that the script was
|
||||||
|
# actually loaded. This could probably be changed to just hold the GutTest
|
||||||
|
# script that was loaded, cutting down on complexity elsewhere.
|
||||||
|
var is_loaded = false
|
||||||
|
|
||||||
|
# Set by Gut when it decides that a script should be skipped.
|
||||||
|
# Right now this is whenever the script has the variable skip_script declared.
|
||||||
|
# the value of skip_script is put into skip_reason.
|
||||||
|
var was_skipped = false
|
||||||
|
var skip_reason = ""
|
||||||
|
var was_run = false
|
||||||
|
|
||||||
|
var name = "":
|
||||||
|
get:
|
||||||
|
return path
|
||||||
|
set(val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _init(utils = null, logger = null):
|
||||||
|
_utils = utils
|
||||||
|
_lgr = logger
|
||||||
|
|
||||||
|
|
||||||
|
func get_new():
|
||||||
|
return load_script().new()
|
||||||
|
|
||||||
|
|
||||||
|
func load_script():
|
||||||
|
var to_return = load(path)
|
||||||
|
|
||||||
|
if inner_class_name != null and inner_class_name != "":
|
||||||
|
# If we wanted to do inner classes in inner classses
|
||||||
|
# then this would have to become some kind of loop or recursive
|
||||||
|
# call to go all the way down the chain or this class would
|
||||||
|
# have to change to hold onto the loaded class instead of
|
||||||
|
# just path information.
|
||||||
|
to_return = to_return.get(inner_class_name)
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# script.gd.InnerClass
|
||||||
|
func get_filename_and_inner():
|
||||||
|
var to_return = get_filename()
|
||||||
|
if inner_class_name != "":
|
||||||
|
to_return += "." + String(inner_class_name)
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# res://foo/bar.gd.FooBar
|
||||||
|
func get_full_name():
|
||||||
|
var to_return = path
|
||||||
|
if inner_class_name != "":
|
||||||
|
to_return += "." + String(inner_class_name)
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
func get_filename():
|
||||||
|
return path.get_file()
|
||||||
|
|
||||||
|
|
||||||
|
func has_inner_class():
|
||||||
|
return inner_class_name != ""
|
||||||
|
|
||||||
|
|
||||||
|
# Note: although this no longer needs to export the inner_class names since
|
||||||
|
# they are pulled from metadata now, it is easier to leave that in
|
||||||
|
# so we don't have to cut the export down to unique script names.
|
||||||
|
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(source_path):
|
||||||
|
var to_return = source_path
|
||||||
|
if !_utils.file_exists(source_path):
|
||||||
|
_lgr.debug("Checking for remap for: " + source_path)
|
||||||
|
var remap_path = source_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)
|
||||||
|
# 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 = StringName("")
|
||||||
|
|
||||||
|
|
||||||
|
func get_test_named(name):
|
||||||
|
return _utils.search_array(tests, "name", name)
|
||||||
|
|
||||||
|
|
||||||
|
func mark_tests_to_skip_with_suffix(suffix):
|
||||||
|
for single_test in tests:
|
||||||
|
single_test.should_skip = single_test.name.ends_with(suffix)
|
||||||
|
|
||||||
|
|
||||||
|
func get_ran_test_count():
|
||||||
|
var count = 0
|
||||||
|
for t in tests:
|
||||||
|
if t.was_run:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_assert_count():
|
||||||
|
var count = 0
|
||||||
|
for t in tests:
|
||||||
|
count += t.pass_texts.size()
|
||||||
|
count += t.fail_texts.size()
|
||||||
|
for t in setup_teardown_tests:
|
||||||
|
count += t.pass_texts.size()
|
||||||
|
count += t.fail_texts.size()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_pass_count():
|
||||||
|
var count = 0
|
||||||
|
for t in tests:
|
||||||
|
count += t.pass_texts.size()
|
||||||
|
for t in setup_teardown_tests:
|
||||||
|
count += t.pass_texts.size()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_fail_count():
|
||||||
|
var count = 0
|
||||||
|
for t in tests:
|
||||||
|
count += t.fail_texts.size()
|
||||||
|
for t in setup_teardown_tests:
|
||||||
|
count += t.fail_texts.size()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_pending_count():
|
||||||
|
var count = 0
|
||||||
|
for t in tests:
|
||||||
|
count += t.pending_texts.size()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_passing_test_count():
|
||||||
|
var count = 0
|
||||||
|
for t in tests:
|
||||||
|
if t.is_passing():
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_failing_test_count():
|
||||||
|
var count = 0
|
||||||
|
for t in tests:
|
||||||
|
if t.is_failing():
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_risky_count():
|
||||||
|
var count = 0
|
||||||
|
if was_skipped:
|
||||||
|
count = 1
|
||||||
|
else:
|
||||||
|
for t in tests:
|
||||||
|
if t.is_risky():
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
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].to_s())
|
||||||
|
return to_return
|
117
addons/gut/collected_test.gd
Normal file
117
addons/gut/collected_test.gd
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Used to keep track of info about each test ran.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# the name of the function
|
||||||
|
var name = ""
|
||||||
|
|
||||||
|
# flag to know if the name has been printed yet. Used by the logger.
|
||||||
|
var has_printed_name = false
|
||||||
|
|
||||||
|
# the number of arguments the method has
|
||||||
|
var arg_count = 0
|
||||||
|
|
||||||
|
# The number of asserts in the test. Converted to a property for backwards
|
||||||
|
# compatibility. This now reflects the text sizes instead of being a value
|
||||||
|
# that can be altered externally.
|
||||||
|
var assert_count = 0:
|
||||||
|
get:
|
||||||
|
return pass_texts.size() + fail_texts.size()
|
||||||
|
set(val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Converted to propety for backwards compatibility. This now cannot be set
|
||||||
|
# externally
|
||||||
|
var pending = false:
|
||||||
|
get:
|
||||||
|
return is_pending()
|
||||||
|
set(val):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# the line number when the test fails
|
||||||
|
var line_number = -1
|
||||||
|
|
||||||
|
# Set internally by Gut using whatever reason Gut wants to use to set this.
|
||||||
|
# Gut will skip these marked true and the test will be listed as risky.
|
||||||
|
var should_skip = false
|
||||||
|
|
||||||
|
var pass_texts = []
|
||||||
|
var fail_texts = []
|
||||||
|
var pending_texts = []
|
||||||
|
var orphans = 0
|
||||||
|
|
||||||
|
var was_run = false
|
||||||
|
|
||||||
|
|
||||||
|
func did_pass():
|
||||||
|
return is_passing()
|
||||||
|
|
||||||
|
|
||||||
|
func add_fail(fail_text):
|
||||||
|
fail_texts.append(fail_text)
|
||||||
|
|
||||||
|
|
||||||
|
func add_pending(pending_text):
|
||||||
|
pending_texts.append(pending_text)
|
||||||
|
|
||||||
|
|
||||||
|
func add_pass(passing_text):
|
||||||
|
pass_texts.append(passing_text)
|
||||||
|
|
||||||
|
|
||||||
|
# must have passed an assert and not have any other status to be passing
|
||||||
|
func is_passing():
|
||||||
|
return pass_texts.size() > 0 and fail_texts.size() == 0 and pending_texts.size() == 0
|
||||||
|
|
||||||
|
|
||||||
|
# failing takes precedence over everything else, so any failures makes the
|
||||||
|
# test a failure.
|
||||||
|
func is_failing():
|
||||||
|
return fail_texts.size() > 0
|
||||||
|
|
||||||
|
|
||||||
|
# test is only pending if pending was called and the test is not failing.
|
||||||
|
func is_pending():
|
||||||
|
return pending_texts.size() > 0 and fail_texts.size() == 0
|
||||||
|
|
||||||
|
|
||||||
|
func is_risky():
|
||||||
|
return should_skip or (was_run and !did_something())
|
||||||
|
|
||||||
|
|
||||||
|
func did_something():
|
||||||
|
return is_passing() or is_failing() or is_pending()
|
||||||
|
|
||||||
|
|
||||||
|
func get_status_text():
|
||||||
|
var to_return = GutUtils.TEST_STATUSES.NO_ASSERTS
|
||||||
|
|
||||||
|
if should_skip:
|
||||||
|
to_return = GutUtils.TEST_STATUSES.SKIPPED
|
||||||
|
elif !was_run:
|
||||||
|
to_return = GutUtils.TEST_STATUSES.NOT_RUN
|
||||||
|
elif pending_texts.size() > 0:
|
||||||
|
to_return = GutUtils.TEST_STATUSES.PENDING
|
||||||
|
elif fail_texts.size() > 0:
|
||||||
|
to_return = GutUtils.TEST_STATUSES.FAILED
|
||||||
|
elif pass_texts.size() > 0:
|
||||||
|
to_return = GutUtils.TEST_STATUSES.PASSED
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# Deprecated
|
||||||
|
func get_status():
|
||||||
|
return get_status_text()
|
||||||
|
|
||||||
|
|
||||||
|
func to_s():
|
||||||
|
var pad = " "
|
||||||
|
var to_return = str(name, "[", get_status_text(), "]\n")
|
||||||
|
|
||||||
|
for i in range(fail_texts.size()):
|
||||||
|
to_return += str(pad, "Fail: ", fail_texts[i])
|
||||||
|
for i in range(pending_texts.size()):
|
||||||
|
to_return += str(pad, "Pending: ", pending_texts[i], "\n")
|
||||||
|
for i in range(pass_texts.size()):
|
||||||
|
to_return += str(pad, "Pass: ", pass_texts[i], "\n")
|
||||||
|
return to_return
|
|
@ -4,7 +4,6 @@ var _max_length = 100
|
||||||
var _should_compare_int_to_float = true
|
var _should_compare_int_to_float = true
|
||||||
|
|
||||||
const MISSING = "|__missing__gut__compare__value__|"
|
const MISSING = "|__missing__gut__compare__value__|"
|
||||||
const DICTIONARY_DISCLAIMER = "Dictionaries are compared-by-ref. See assert_eq in wiki."
|
|
||||||
|
|
||||||
|
|
||||||
func _cannot_compare_text(v1, v2):
|
func _cannot_compare_text(v1, v2):
|
||||||
|
@ -59,19 +58,12 @@ func simple(v1, v2, missing_string = ""):
|
||||||
result.are_equal = v1 == v2
|
result.are_equal = v1 == v2
|
||||||
elif _utils.are_datatypes_same(v1, v2):
|
elif _utils.are_datatypes_same(v1, v2):
|
||||||
result.are_equal = v1 == v2
|
result.are_equal = v1 == v2
|
||||||
if typeof(v1) == TYPE_DICTIONARY:
|
|
||||||
if result.are_equal:
|
|
||||||
extra = ". Same dictionary ref. "
|
|
||||||
else:
|
|
||||||
extra = ". Different dictionary refs. "
|
|
||||||
extra += DICTIONARY_DISCLAIMER
|
|
||||||
|
|
||||||
if typeof(v1) == TYPE_ARRAY:
|
|
||||||
var array_result = _utils.DiffTool.new(v1, v2, _utils.DIFF.SHALLOW)
|
|
||||||
result.summary = array_result.get_short_summary()
|
|
||||||
if !array_result.are_equal:
|
|
||||||
extra = ".\n" + array_result.get_short_summary()
|
|
||||||
|
|
||||||
|
if typeof(v1) == TYPE_DICTIONARY or typeof(v1) == TYPE_ARRAY:
|
||||||
|
var sub_result = _utils.DiffTool.new(v1, v2, _utils.DIFF.DEEP)
|
||||||
|
result.summary = sub_result.get_short_summary()
|
||||||
|
if !sub_result.are_equal:
|
||||||
|
extra = ".\n" + sub_result.get_short_summary()
|
||||||
else:
|
else:
|
||||||
cmp_str = "!="
|
cmp_str = "!="
|
||||||
result.are_equal = false
|
result.are_equal = false
|
||||||
|
@ -85,10 +77,9 @@ func simple(v1, v2, missing_string = ""):
|
||||||
|
|
||||||
func shallow(v1, v2):
|
func shallow(v1, v2):
|
||||||
var result = null
|
var result = null
|
||||||
|
|
||||||
if _utils.are_datatypes_same(v1, v2):
|
if _utils.are_datatypes_same(v1, v2):
|
||||||
if typeof(v1) in [TYPE_ARRAY, TYPE_DICTIONARY]:
|
if typeof(v1) in [TYPE_ARRAY, TYPE_DICTIONARY]:
|
||||||
result = _utils.DiffTool.new(v1, v2, _utils.DIFF.SHALLOW)
|
result = _utils.DiffTool.new(v1, v2, _utils.DIFF.DEEP)
|
||||||
else:
|
else:
|
||||||
result = simple(v1, v2)
|
result = simple(v1, v2)
|
||||||
else:
|
else:
|
||||||
|
@ -119,8 +110,6 @@ func compare(v1, v2, diff_type = _utils.DIFF.SIMPLE):
|
||||||
var result = null
|
var result = null
|
||||||
if diff_type == _utils.DIFF.SIMPLE:
|
if diff_type == _utils.DIFF.SIMPLE:
|
||||||
result = simple(v1, v2)
|
result = simple(v1, v2)
|
||||||
elif diff_type == _utils.DIFF.SHALLOW:
|
|
||||||
result = shallow(v1, v2)
|
|
||||||
elif diff_type == _utils.DIFF.DEEP:
|
elif diff_type == _utils.DIFF.DEEP:
|
||||||
result = deep(v1, v2)
|
result = deep(v1, v2)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
extends "res://addons/gut/compare_result.gd"
|
extends "res://addons/gut/compare_result.gd"
|
||||||
const INDENT = " "
|
const INDENT = " "
|
||||||
enum { DEEP, SHALLOW, SIMPLE }
|
enum { DEEP, SIMPLE }
|
||||||
|
|
||||||
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
||||||
var _strutils = _utils.Strutils.new()
|
var _strutils = _utils.Strutils.new()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{func_decleration}
|
{func_decleration}
|
||||||
__gutdbl.spy_on('{method_name}', {param_array})
|
{vararg_warning}__gutdbl.spy_on('{method_name}', {param_array})
|
||||||
if(__gutdbl.should_call_super('{method_name}', {param_array})):
|
if(__gutdbl.should_call_super('{method_name}', {param_array})):
|
||||||
return {super_call}
|
return {super_call}
|
||||||
else:
|
else:
|
||||||
return __gutdbl.get_stubbed_return('{method_name}', {param_array})
|
return __gutdbl.get_stubbed_return('{method_name}', {param_array})
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ var __gutdbl_values = {
|
||||||
gut = {gut_id},
|
gut = {gut_id},
|
||||||
from_singleton = '{singleton_name}',
|
from_singleton = '{singleton_name}',
|
||||||
is_partial = {is_partial},
|
is_partial = {is_partial},
|
||||||
|
doubled_methods = {doubled_methods},
|
||||||
}
|
}
|
||||||
var __gutdbl = load('res://addons/gut/double_tools.gd').new(__gutdbl_values)
|
var __gutdbl = load('res://addons/gut/double_tools.gd').new(__gutdbl_values)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,21 @@ var double = null
|
||||||
const NO_DEFAULT_VALUE = "!__gut__no__default__value__!"
|
const NO_DEFAULT_VALUE = "!__gut__no__default__value__!"
|
||||||
|
|
||||||
|
|
||||||
|
func _init(values = null):
|
||||||
|
if values != null:
|
||||||
|
double = values.double
|
||||||
|
thepath = values.thepath
|
||||||
|
subpath = values.subpath
|
||||||
|
stubber = from_id(values.stubber)
|
||||||
|
spy = from_id(values.spy)
|
||||||
|
gut = from_id(values.gut)
|
||||||
|
from_singleton = values.from_singleton
|
||||||
|
is_partial = values.is_partial
|
||||||
|
|
||||||
|
if gut != null:
|
||||||
|
gut.get_autofree().add_free(double)
|
||||||
|
|
||||||
|
|
||||||
func from_id(inst_id):
|
func from_id(inst_id):
|
||||||
if inst_id == -1:
|
if inst_id == -1:
|
||||||
return null
|
return null
|
||||||
|
@ -43,16 +58,13 @@ func default_val(method_name, p_index, default_val = NO_DEFAULT_VALUE):
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
||||||
func _init(values = null):
|
func vararg_warning():
|
||||||
if values != null:
|
|
||||||
double = values.double
|
|
||||||
thepath = values.thepath
|
|
||||||
subpath = values.subpath
|
|
||||||
stubber = from_id(values.stubber)
|
|
||||||
spy = from_id(values.spy)
|
|
||||||
gut = from_id(values.gut)
|
|
||||||
from_singleton = values.from_singleton
|
|
||||||
is_partial = values.is_partial
|
|
||||||
|
|
||||||
if gut != null:
|
if gut != null:
|
||||||
gut.get_autofree().add_free(double)
|
gut.get_logger().warn(
|
||||||
|
(
|
||||||
|
"This method contains a vararg argument and the paramter count was not stubbed. "
|
||||||
|
+ "GUT adds extra parameters to this method which should fill most needs. "
|
||||||
|
+ "It is recommended that you stub param_count for this object's class to ensure "
|
||||||
|
+ "that there are not any parameter count mismatch errors."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -121,7 +121,10 @@ func get_strategy():
|
||||||
|
|
||||||
|
|
||||||
func set_strategy(strategy):
|
func set_strategy(strategy):
|
||||||
|
if GutUtils.DOUBLE_STRATEGY.values().has(strategy):
|
||||||
_strategy = strategy
|
_strategy = strategy
|
||||||
|
else:
|
||||||
|
_lgr.error(str("doubler.gd: invalid double strategy ", strategy))
|
||||||
|
|
||||||
|
|
||||||
var _method_maker = _utils.MethodMaker.new()
|
var _method_maker = _utils.MethodMaker.new()
|
||||||
|
@ -154,7 +157,7 @@ func _get_indented_line(indents, text):
|
||||||
|
|
||||||
|
|
||||||
func _stub_to_call_super(parsed, method_name):
|
func _stub_to_call_super(parsed, method_name):
|
||||||
if _utils.non_super_methods.has(method_name):
|
if !parsed.get_method(method_name).is_eligible_for_doubling():
|
||||||
return
|
return
|
||||||
|
|
||||||
var params = _utils.StubParams.new(parsed.script_path, method_name, parsed.subpath)
|
var params = _utils.StubParams.new(parsed.script_path, method_name, parsed.subpath)
|
||||||
|
@ -162,7 +165,7 @@ func _stub_to_call_super(parsed, method_name):
|
||||||
_stubber.add_stub(params)
|
_stubber.add_stub(params)
|
||||||
|
|
||||||
|
|
||||||
func _get_base_script_text(parsed, override_path, partial):
|
func _get_base_script_text(parsed, override_path, partial, included_methods):
|
||||||
var path = parsed.script_path
|
var path = parsed.script_path
|
||||||
if override_path != null:
|
if override_path != null:
|
||||||
path = override_path
|
path = override_path
|
||||||
|
@ -188,44 +191,74 @@ func _get_base_script_text(parsed, override_path, partial):
|
||||||
"properties": "", #obj_info.get_properties_text(),
|
"properties": "", #obj_info.get_properties_text(),
|
||||||
# metadata values
|
# metadata values
|
||||||
"path": path,
|
"path": path,
|
||||||
"subpath": _utils.nvl(parsed.subpath, ""),
|
"subpath": GutUtils.nvl(parsed.subpath, ""),
|
||||||
"stubber_id": stubber_id,
|
"stubber_id": stubber_id,
|
||||||
"spy_id": spy_id,
|
"spy_id": spy_id,
|
||||||
"gut_id": gut_id,
|
"gut_id": gut_id,
|
||||||
"singleton_name": "", #_utils.nvl(obj_info.get_singleton_name(), ''),
|
"singleton_name": "", #GutUtils.nvl(obj_info.get_singleton_name(), ''),
|
||||||
"is_partial": partial,
|
"is_partial": partial,
|
||||||
|
"doubled_methods": included_methods,
|
||||||
}
|
}
|
||||||
|
|
||||||
return _base_script_text.format(values)
|
return _base_script_text.format(values)
|
||||||
|
|
||||||
|
|
||||||
|
func _is_method_eligible_for_doubling(parsed_script, parsed_method):
|
||||||
|
return (
|
||||||
|
!parsed_method.is_accessor()
|
||||||
|
and parsed_method.is_eligible_for_doubling()
|
||||||
|
and !_ignored_methods.has(parsed_script.resource, parsed_method.meta.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Disable the native_method_override setting so that doubles do not generate
|
||||||
|
# errors or warnings when doubling with INCLUDE_NATIVE or when a method has
|
||||||
|
# been added because of param_count stub.
|
||||||
|
func _create_script_no_warnings(src):
|
||||||
|
var prev_native_override_value = null
|
||||||
|
var native_method_override = "debug/gdscript/warnings/native_method_override"
|
||||||
|
prev_native_override_value = ProjectSettings.get_setting(native_method_override)
|
||||||
|
ProjectSettings.set_setting(native_method_override, 0)
|
||||||
|
|
||||||
|
var DblClass = _utils.create_script_from_source(src)
|
||||||
|
|
||||||
|
ProjectSettings.set_setting(native_method_override, prev_native_override_value)
|
||||||
|
return DblClass
|
||||||
|
|
||||||
|
|
||||||
func _create_double(parsed, strategy, override_path, partial):
|
func _create_double(parsed, strategy, override_path, partial):
|
||||||
var base_script = _get_base_script_text(parsed, override_path, partial)
|
|
||||||
var super_name = ""
|
|
||||||
var path = ""
|
var path = ""
|
||||||
|
|
||||||
path = parsed.script_path
|
path = parsed.script_path
|
||||||
var dbl_src = ""
|
var dbl_src = ""
|
||||||
dbl_src += base_script
|
var included_methods = []
|
||||||
|
|
||||||
for method in parsed.get_local_methods():
|
for method in parsed.get_local_methods():
|
||||||
if !method.is_black_listed() && !_ignored_methods.has(parsed.resource, method.meta.name):
|
if _is_method_eligible_for_doubling(parsed, method):
|
||||||
|
included_methods.append(method.meta.name)
|
||||||
var mthd = parsed.get_local_method(method.meta.name)
|
var mthd = parsed.get_local_method(method.meta.name)
|
||||||
dbl_src += _get_func_text(method.meta, path, super_name)
|
if parsed.is_native:
|
||||||
|
dbl_src += _get_func_text(method.meta, parsed.resource)
|
||||||
|
else:
|
||||||
|
dbl_src += _get_func_text(method.meta, path)
|
||||||
|
|
||||||
if strategy == _utils.DOUBLE_STRATEGY.INCLUDE_SUPER:
|
if strategy == _utils.DOUBLE_STRATEGY.INCLUDE_NATIVE:
|
||||||
for method in parsed.get_super_methods():
|
for method in parsed.get_super_methods():
|
||||||
if (
|
if _is_method_eligible_for_doubling(parsed, method):
|
||||||
!method.is_black_listed()
|
included_methods.append(method.meta.name)
|
||||||
&& !_ignored_methods.has(parsed.resource, method.meta.name)
|
|
||||||
):
|
|
||||||
_stub_to_call_super(parsed, method.meta.name)
|
_stub_to_call_super(parsed, method.meta.name)
|
||||||
dbl_src += _get_func_text(method.meta, path, super_name)
|
if parsed.is_native:
|
||||||
|
dbl_src += _get_func_text(method.meta, parsed.resource)
|
||||||
|
else:
|
||||||
|
dbl_src += _get_func_text(method.meta, path)
|
||||||
|
|
||||||
|
var base_script = _get_base_script_text(parsed, override_path, partial, included_methods)
|
||||||
|
dbl_src = base_script + "\n\n" + dbl_src
|
||||||
|
|
||||||
if print_source:
|
if print_source:
|
||||||
print(_utils.add_line_numbers(dbl_src))
|
print(_utils.add_line_numbers(dbl_src))
|
||||||
|
|
||||||
var DblClass = _utils.create_script_from_source(dbl_src)
|
var DblClass = _create_script_no_warnings(dbl_src)
|
||||||
if _stubber != null:
|
if _stubber != null:
|
||||||
_stub_method_default_values(DblClass, parsed, strategy)
|
_stub_method_default_values(DblClass, parsed, strategy)
|
||||||
|
|
||||||
|
@ -234,7 +267,10 @@ func _create_double(parsed, strategy, override_path, partial):
|
||||||
|
|
||||||
func _stub_method_default_values(which, parsed, strategy):
|
func _stub_method_default_values(which, parsed, strategy):
|
||||||
for method in parsed.get_local_methods():
|
for method in parsed.get_local_methods():
|
||||||
if !method.is_black_listed() && !_ignored_methods.has(parsed.resource, method.meta.name):
|
if (
|
||||||
|
method.is_eligible_for_doubling()
|
||||||
|
&& !_ignored_methods.has(parsed.resource, method.meta.name)
|
||||||
|
):
|
||||||
_stubber.stub_defaults_from_meta(parsed.script_path, method.meta)
|
_stubber.stub_defaults_from_meta(parsed.script_path, method.meta)
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,7 +278,7 @@ func _double_scene_and_script(scene, strategy, partial):
|
||||||
var to_return = PackedSceneDouble.new()
|
var to_return = PackedSceneDouble.new()
|
||||||
to_return.load_scene(scene.get_path())
|
to_return.load_scene(scene.get_path())
|
||||||
|
|
||||||
var script_obj = _utils.get_scene_script_object(scene)
|
var script_obj = GutUtils.get_scene_script_object(scene)
|
||||||
if script_obj != null:
|
if script_obj != null:
|
||||||
var script_dbl = null
|
var script_dbl = null
|
||||||
if partial:
|
if partial:
|
||||||
|
@ -261,14 +297,12 @@ func _get_inst_id_ref_str(inst):
|
||||||
return ref_str
|
return ref_str
|
||||||
|
|
||||||
|
|
||||||
func _get_func_text(method_hash, path, super_ = ""):
|
func _get_func_text(method_hash, path):
|
||||||
var override_count = null
|
var override_count = null
|
||||||
if _stubber != null:
|
if _stubber != null:
|
||||||
override_count = _stubber.get_parameter_count(path, method_hash.name)
|
override_count = _stubber.get_parameter_count(path, method_hash.name)
|
||||||
if override_count != null:
|
|
||||||
print(method_hash.name, " override: ", override_count)
|
|
||||||
|
|
||||||
var text = _method_maker.get_function_text(method_hash, path, override_count, super_) + "\n"
|
var text = _method_maker.get_function_text(method_hash, override_count) + "\n"
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -329,11 +363,11 @@ func partial_double_scene(scene, strategy = _strategy):
|
||||||
|
|
||||||
|
|
||||||
func double_gdnative(which):
|
func double_gdnative(which):
|
||||||
return _double(which, _utils.DOUBLE_STRATEGY.INCLUDE_SUPER)
|
return _double(which, _utils.DOUBLE_STRATEGY.INCLUDE_NATIVE)
|
||||||
|
|
||||||
|
|
||||||
func partial_double_gdnative(which):
|
func partial_double_gdnative(which):
|
||||||
return _partial_double(which, _utils.DOUBLE_STRATEGY.INCLUDE_SUPER)
|
return _partial_double(which, _utils.DOUBLE_STRATEGY.INCLUDE_NATIVE)
|
||||||
|
|
||||||
|
|
||||||
func double_inner(parent, inner, strategy = _strategy):
|
func double_inner(parent, inner, strategy = _strategy):
|
||||||
|
|
|
@ -1,232 +1,150 @@
|
||||||
[gd_scene load_steps=3 format=2]
|
[gd_scene load_steps=3 format=3 uid="uid://bsk32dh41b4gs"]
|
||||||
|
|
||||||
[ext_resource path="res://addons/gut/gui/ShortcutButton.tscn" type="PackedScene" id=1]
|
[ext_resource type="PackedScene" uid="uid://sfb1fw8j6ufu" path="res://addons/gut/gui/ShortcutButton.tscn" id="1"]
|
||||||
[ext_resource path="res://addons/gut/gui/BottomPanelShortcuts.gd" type="Script" id=2]
|
[ext_resource type="Script" path="res://addons/gut/gui/BottomPanelShortcuts.gd" id="2"]
|
||||||
|
|
||||||
[node name="BottomPanelShortcuts" type="Window"]
|
[node name="BottomPanelShortcuts" type="Popup"]
|
||||||
|
title = "Shortcuts"
|
||||||
|
size = Vector2i(500, 350)
|
||||||
visible = true
|
visible = true
|
||||||
anchor_right = 0.234
|
|
||||||
anchor_bottom = 0.328
|
|
||||||
offset_right = 195.384
|
|
||||||
offset_bottom = 62.2
|
|
||||||
custom_minimum_size = Vector2( 435, 305 )
|
|
||||||
exclusive = true
|
exclusive = true
|
||||||
window_title = "GUT Shortcuts"
|
script = ExtResource("2")
|
||||||
resizable = true
|
|
||||||
script = ExtResource( 2 )
|
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="Layout" type="VBoxContainer" parent="."]
|
[node name="Layout" type="VBoxContainer" parent="."]
|
||||||
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_left = 5.0
|
offset_left = 5.0
|
||||||
offset_right = -5.0
|
offset_right = -5.0
|
||||||
offset_bottom = 2.0
|
offset_bottom = 2.0
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="TopPad" type="CenterContainer" parent="Layout"]
|
[node name="TopPad" type="CenterContainer" parent="Layout"]
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 5.0
|
|
||||||
custom_minimum_size = Vector2(0, 5)
|
custom_minimum_size = Vector2(0, 5)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label2" type="Label" parent="Layout"]
|
[node name="Label2" type="Label" parent="Layout"]
|
||||||
offset_top = 9.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 29.0
|
|
||||||
custom_minimum_size = Vector2(0, 20)
|
custom_minimum_size = Vector2(0, 20)
|
||||||
|
layout_mode = 2
|
||||||
text = "Always Active"
|
text = "Always Active"
|
||||||
align = 1
|
|
||||||
valign = 1
|
|
||||||
autowrap = true
|
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="Layout/Label2"]
|
[node name="ColorRect" type="ColorRect" parent="Layout/Label2"]
|
||||||
show_behind_parent = true
|
show_behind_parent = true
|
||||||
|
layout_mode = 0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
color = Color(0, 0, 0, 0.196078)
|
color = Color(0, 0, 0, 0.196078)
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="CPanelButton" type="HBoxContainer" parent="Layout"]
|
[node name="CPanelButton" type="HBoxContainer" parent="Layout"]
|
||||||
offset_top = 33.0
|
layout_mode = 2
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 58.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Layout/CPanelButton"]
|
[node name="Label" type="Label" parent="Layout/CPanelButton"]
|
||||||
offset_right = 138.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
size_flags_vertical = 7
|
size_flags_vertical = 7
|
||||||
text = "Show/Hide GUT Panel"
|
text = "Show/Hide GUT Panel"
|
||||||
valign = 1
|
|
||||||
|
|
||||||
[node name="ShortcutButton" parent="Layout/CPanelButton" instance=ExtResource( 1 )]
|
[node name="ShortcutButton" parent="Layout/CPanelButton" instance=ExtResource("1")]
|
||||||
anchor_right = 0.0
|
layout_mode = 2
|
||||||
anchor_bottom = 0.0
|
|
||||||
offset_left = 142.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="GutPanelPad" type="CenterContainer" parent="Layout"]
|
[node name="GutPanelPad" type="CenterContainer" parent="Layout"]
|
||||||
offset_top = 62.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 67.0
|
|
||||||
custom_minimum_size = Vector2(0, 5)
|
custom_minimum_size = Vector2(0, 5)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Layout"]
|
[node name="Label" type="Label" parent="Layout"]
|
||||||
offset_top = 71.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 91.0
|
|
||||||
custom_minimum_size = Vector2(0, 20)
|
custom_minimum_size = Vector2(0, 20)
|
||||||
|
layout_mode = 2
|
||||||
text = "Only Active When GUT Panel Shown"
|
text = "Only Active When GUT Panel Shown"
|
||||||
align = 1
|
|
||||||
valign = 1
|
|
||||||
autowrap = true
|
|
||||||
|
|
||||||
[node name="ColorRect2" type="ColorRect" parent="Layout/Label"]
|
[node name="ColorRect2" type="ColorRect" parent="Layout/Label"]
|
||||||
show_behind_parent = true
|
show_behind_parent = true
|
||||||
|
layout_mode = 0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
color = Color(0, 0, 0, 0.196078)
|
color = Color(0, 0, 0, 0.196078)
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="TopPad2" type="CenterContainer" parent="Layout"]
|
[node name="TopPad2" type="CenterContainer" parent="Layout"]
|
||||||
offset_top = 95.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 100.0
|
|
||||||
custom_minimum_size = Vector2(0, 5)
|
custom_minimum_size = Vector2(0, 5)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="CRunAll" type="HBoxContainer" parent="Layout"]
|
[node name="CRunAll" type="HBoxContainer" parent="Layout"]
|
||||||
offset_top = 104.0
|
layout_mode = 2
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 129.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Layout/CRunAll"]
|
[node name="Label" type="Label" parent="Layout/CRunAll"]
|
||||||
offset_right = 50.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
size_flags_vertical = 7
|
size_flags_vertical = 7
|
||||||
text = "Run All"
|
text = "Run All"
|
||||||
valign = 1
|
|
||||||
|
|
||||||
[node name="ShortcutButton" parent="Layout/CRunAll" instance=ExtResource( 1 )]
|
[node name="ShortcutButton" parent="Layout/CRunAll" instance=ExtResource("1")]
|
||||||
anchor_right = 0.0
|
layout_mode = 2
|
||||||
anchor_bottom = 0.0
|
|
||||||
offset_left = 54.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="CRunCurrentScript" type="HBoxContainer" parent="Layout"]
|
[node name="CRunCurrentScript" type="HBoxContainer" parent="Layout"]
|
||||||
offset_top = 133.0
|
layout_mode = 2
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 158.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Layout/CRunCurrentScript"]
|
[node name="Label" type="Label" parent="Layout/CRunCurrentScript"]
|
||||||
offset_right = 115.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
size_flags_vertical = 7
|
size_flags_vertical = 7
|
||||||
text = "Run Current Script"
|
text = "Run Current Script"
|
||||||
valign = 1
|
|
||||||
|
|
||||||
[node name="ShortcutButton" parent="Layout/CRunCurrentScript" instance=ExtResource( 1 )]
|
[node name="ShortcutButton" parent="Layout/CRunCurrentScript" instance=ExtResource("1")]
|
||||||
anchor_right = 0.0
|
layout_mode = 2
|
||||||
anchor_bottom = 0.0
|
|
||||||
offset_left = 119.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="CRunCurrentInner" type="HBoxContainer" parent="Layout"]
|
[node name="CRunCurrentInner" type="HBoxContainer" parent="Layout"]
|
||||||
offset_top = 162.0
|
layout_mode = 2
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 187.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Layout/CRunCurrentInner"]
|
[node name="Label" type="Label" parent="Layout/CRunCurrentInner"]
|
||||||
offset_right = 150.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
size_flags_vertical = 7
|
size_flags_vertical = 7
|
||||||
text = "Run Current Inner Class"
|
text = "Run Current Inner Class"
|
||||||
valign = 1
|
|
||||||
|
|
||||||
[node name="ShortcutButton" parent="Layout/CRunCurrentInner" instance=ExtResource( 1 )]
|
[node name="ShortcutButton" parent="Layout/CRunCurrentInner" instance=ExtResource("1")]
|
||||||
anchor_right = 0.0
|
layout_mode = 2
|
||||||
anchor_bottom = 0.0
|
|
||||||
offset_left = 154.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="CRunCurrentTest" type="HBoxContainer" parent="Layout"]
|
[node name="CRunCurrentTest" type="HBoxContainer" parent="Layout"]
|
||||||
offset_top = 191.0
|
layout_mode = 2
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 216.0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Layout/CRunCurrentTest"]
|
[node name="Label" type="Label" parent="Layout/CRunCurrentTest"]
|
||||||
offset_right = 106.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
size_flags_vertical = 7
|
size_flags_vertical = 7
|
||||||
text = "Run Current Test"
|
text = "Run Current Test"
|
||||||
valign = 1
|
|
||||||
|
|
||||||
[node name="ShortcutButton" parent="Layout/CRunCurrentTest" instance=ExtResource( 1 )]
|
[node name="ShortcutButton" parent="Layout/CRunCurrentTest" instance=ExtResource("1")]
|
||||||
anchor_right = 0.0
|
layout_mode = 2
|
||||||
anchor_bottom = 0.0
|
|
||||||
offset_left = 110.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="CenterContainer2" type="CenterContainer" parent="Layout"]
|
[node name="CenterContainer2" type="CenterContainer" parent="Layout"]
|
||||||
offset_top = 220.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 241.0
|
|
||||||
custom_minimum_size = Vector2(0, 5)
|
custom_minimum_size = Vector2(0, 5)
|
||||||
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="ShiftDisclaimer" type="Label" parent="Layout"]
|
[node name="ShiftDisclaimer" type="Label" parent="Layout"]
|
||||||
offset_top = 245.0
|
layout_mode = 2
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 259.0
|
|
||||||
text = "\"Shift\" cannot be the only modifier for a shortcut."
|
text = "\"Shift\" cannot be the only modifier for a shortcut."
|
||||||
align = 2
|
|
||||||
autowrap = true
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Layout"]
|
[node name="HBoxContainer" type="HBoxContainer" parent="Layout"]
|
||||||
offset_top = 263.0
|
layout_mode = 2
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 293.0
|
|
||||||
|
|
||||||
[node name="CenterContainer" type="CenterContainer" parent="Layout/HBoxContainer"]
|
[node name="CenterContainer" type="CenterContainer" parent="Layout/HBoxContainer"]
|
||||||
offset_right = 361.0
|
layout_mode = 2
|
||||||
offset_bottom = 30.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="Hide" type="Button" parent="Layout/HBoxContainer"]
|
[node name="Hide" type="Button" parent="Layout/HBoxContainer"]
|
||||||
offset_left = 365.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 30.0
|
|
||||||
custom_minimum_size = Vector2(60, 30)
|
custom_minimum_size = Vector2(60, 30)
|
||||||
|
layout_mode = 2
|
||||||
text = "Close"
|
text = "Close"
|
||||||
|
|
||||||
[node name="BottomPad" type="CenterContainer" parent="Layout"]
|
[node name="BottomPad" type="CenterContainer" parent="Layout"]
|
||||||
offset_top = 297.0
|
|
||||||
offset_right = 425.0
|
|
||||||
offset_bottom = 307.0
|
|
||||||
custom_minimum_size = Vector2(0, 10)
|
custom_minimum_size = Vector2(0, 10)
|
||||||
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[connection signal="pressed" from="Layout/HBoxContainer/Hide" to="." method="_on_Hide_pressed"]
|
[connection signal="pressed" from="Layout/HBoxContainer/Hide" to="." method="_on_Hide_pressed"]
|
||||||
|
|
|
@ -1,28 +1,29 @@
|
||||||
[gd_scene load_steps=10 format=3 uid="uid://b3bostcslstem"]
|
[gd_scene load_steps=10 format=3 uid="uid://b3bostcslstem"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://addons/gut/gui/GutBottomPanel.gd" id="1"]
|
[ext_resource type="Script" path="res://addons/gut/gui/GutBottomPanel.gd" id="1"]
|
||||||
[ext_resource type="PackedScene" path="res://addons/gut/gui/BottomPanelShortcuts.tscn" id="2"]
|
[ext_resource type="PackedScene" uid="uid://bsk32dh41b4gs" path="res://addons/gut/gui/BottomPanelShortcuts.tscn" id="2"]
|
||||||
[ext_resource type="PackedScene" path="res://addons/gut/gui/RunAtCursor.tscn" id="3"]
|
[ext_resource type="PackedScene" uid="uid://0yunjxtaa8iw" path="res://addons/gut/gui/RunAtCursor.tscn" id="3"]
|
||||||
[ext_resource type="Texture2D" path="res://addons/gut/gui/play.png" id="4"]
|
[ext_resource type="Texture2D" uid="uid://cr6tvdv0ve6cv" path="res://addons/gut/gui/play.png" id="4"]
|
||||||
[ext_resource type="PackedScene" path="res://addons/gut/gui/RunResults.tscn" id="5"]
|
[ext_resource type="PackedScene" uid="uid://4gyyn12um08h" path="res://addons/gut/gui/RunResults.tscn" id="5"]
|
||||||
[ext_resource type="PackedScene" path="res://addons/gut/gui/OutputText.tscn" id="6"]
|
[ext_resource type="PackedScene" uid="uid://bqmo4dj64c7yl" path="res://addons/gut/gui/OutputText.tscn" id="6"]
|
||||||
|
|
||||||
[sub_resource type="Shortcut" id="9"]
|
[sub_resource type="Shortcut" id="9"]
|
||||||
|
|
||||||
[sub_resource type="Image" id="Image_r56ab"]
|
[sub_resource type="Image" id="Image_abbh7"]
|
||||||
data = {
|
data = {
|
||||||
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||||
"format": "LumAlpha8",
|
"format": "RGBA8",
|
||||||
"height": 16,
|
"height": 16,
|
||||||
"mipmaps": false,
|
"mipmaps": false,
|
||||||
"width": 16
|
"width": 16
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id="2"]
|
[sub_resource type="ImageTexture" id="ImageTexture_x655i"]
|
||||||
image = SubResource("Image_r56ab")
|
image = SubResource("Image_abbh7")
|
||||||
|
|
||||||
[node name="GutBottomPanel" type="Control"]
|
[node name="GutBottomPanel" type="Control"]
|
||||||
custom_minimum_size = Vector2(250, 250)
|
custom_minimum_size = Vector2(250, 250)
|
||||||
|
layout_mode = 3
|
||||||
anchor_left = -0.0025866
|
anchor_left = -0.0025866
|
||||||
anchor_top = -0.00176575
|
anchor_top = -0.00176575
|
||||||
anchor_right = 0.997413
|
anchor_right = 0.997413
|
||||||
|
@ -34,297 +35,206 @@ offset_bottom = 1.05945
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
|
|
||||||
[node name="layout" type="VBoxContainer" parent="."]
|
[node name="layout" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
|
|
||||||
[node name="ControlBar" type="HBoxContainer" parent="layout"]
|
[node name="ControlBar" type="HBoxContainer" parent="layout"]
|
||||||
offset_right = 1023.0
|
layout_mode = 2
|
||||||
offset_bottom = 31.0
|
|
||||||
|
|
||||||
[node name="RunAll" type="Button" parent="layout/ControlBar"]
|
[node name="RunAll" type="Button" parent="layout/ControlBar"]
|
||||||
offset_right = 85.0
|
layout_mode = 2
|
||||||
offset_bottom = 31.0
|
|
||||||
size_flags_vertical = 11
|
size_flags_vertical = 11
|
||||||
hint_tooltip = "Run all test scripts in the suite."
|
|
||||||
shortcut = SubResource("9")
|
shortcut = SubResource("9")
|
||||||
text = "Run All"
|
text = "Run All"
|
||||||
icon = ExtResource("4")
|
icon = ExtResource("4")
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="layout/ControlBar"]
|
[node name="Label" type="Label" parent="layout/ControlBar"]
|
||||||
offset_left = 89.0
|
layout_mode = 2
|
||||||
offset_top = 2.0
|
|
||||||
offset_right = 162.0
|
|
||||||
offset_bottom = 28.0
|
|
||||||
hint_tooltip = "When a test script is edited, buttons are displayed to
|
|
||||||
run the opened script or an Inner-Test-Class or a
|
|
||||||
single test. The buttons change based on the location
|
|
||||||
of the cursor in the file.
|
|
||||||
|
|
||||||
These buttons will remain active when editing other
|
|
||||||
items so that you can run tests without having to switch
|
|
||||||
back to the test script.
|
|
||||||
|
|
||||||
You can assign keyboard shortcuts for these buttons
|
|
||||||
using the \"shortcuts\" button in the GUT panel."
|
|
||||||
mouse_filter = 1
|
mouse_filter = 1
|
||||||
text = "Current: "
|
text = "Current: "
|
||||||
|
|
||||||
[node name="RunAtCursor" parent="layout/ControlBar" instance=ExtResource("3")]
|
[node name="RunAtCursor" parent="layout/ControlBar" instance=ExtResource("3")]
|
||||||
anchor_right = 0.0
|
layout_mode = 2
|
||||||
anchor_bottom = 0.0
|
|
||||||
offset_left = 166.0
|
|
||||||
offset_right = 532.0
|
|
||||||
offset_bottom = 31.0
|
|
||||||
|
|
||||||
[node name="CenterContainer2" type="CenterContainer" parent="layout/ControlBar"]
|
[node name="CenterContainer2" type="CenterContainer" parent="layout/ControlBar"]
|
||||||
offset_left = 536.0
|
layout_mode = 2
|
||||||
offset_right = 903.0
|
|
||||||
offset_bottom = 31.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="Sep1" type="ColorRect" parent="layout/ControlBar"]
|
[node name="Sep1" type="ColorRect" parent="layout/ControlBar"]
|
||||||
offset_left = 907.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_right = 907.0
|
layout_mode = 2
|
||||||
offset_bottom = 31.0
|
|
||||||
|
|
||||||
[node name="RunResultsBtn" type="Button" parent="layout/ControlBar"]
|
[node name="RunResultsBtn" type="Button" parent="layout/ControlBar"]
|
||||||
offset_left = 911.0
|
layout_mode = 2
|
||||||
offset_right = 935.0
|
|
||||||
offset_bottom = 31.0
|
|
||||||
hint_tooltip = "Show/Hide Results Tree Panel."
|
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
icon = SubResource("2")
|
button_pressed = true
|
||||||
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="OutputBtn" type="Button" parent="layout/ControlBar"]
|
[node name="OutputBtn" type="Button" parent="layout/ControlBar"]
|
||||||
offset_left = 939.0
|
layout_mode = 2
|
||||||
offset_right = 963.0
|
|
||||||
offset_bottom = 31.0
|
|
||||||
hint_tooltip = "Show/Hide Output Panel."
|
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
icon = SubResource("2")
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="Settings" type="Button" parent="layout/ControlBar"]
|
[node name="Settings" type="Button" parent="layout/ControlBar"]
|
||||||
offset_left = 967.0
|
layout_mode = 2
|
||||||
offset_right = 991.0
|
|
||||||
offset_bottom = 31.0
|
|
||||||
hint_tooltip = "Show/Hide Settings Panel."
|
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
icon = SubResource("2")
|
button_pressed = true
|
||||||
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="Sep2" type="ColorRect" parent="layout/ControlBar"]
|
[node name="Sep2" type="ColorRect" parent="layout/ControlBar"]
|
||||||
offset_left = 995.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_right = 995.0
|
layout_mode = 2
|
||||||
offset_bottom = 31.0
|
|
||||||
|
|
||||||
[node name="Shortcuts" type="Button" parent="layout/ControlBar"]
|
[node name="Shortcuts" type="Button" parent="layout/ControlBar"]
|
||||||
offset_left = 999.0
|
layout_mode = 2
|
||||||
offset_right = 1023.0
|
|
||||||
offset_bottom = 31.0
|
|
||||||
size_flags_vertical = 11
|
size_flags_vertical = 11
|
||||||
hint_tooltip = "Set shortcuts for GUT buttons. Shortcuts do not work when the GUT panel is not visible."
|
icon = SubResource("ImageTexture_x655i")
|
||||||
icon = SubResource("2")
|
|
||||||
|
|
||||||
[node name="RSplit" type="HSplitContainer" parent="layout"]
|
[node name="RSplit" type="HSplitContainer" parent="layout"]
|
||||||
offset_top = 35.0
|
layout_mode = 2
|
||||||
offset_right = 1023.0
|
|
||||||
offset_bottom = 599.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
collapsed = true
|
|
||||||
|
|
||||||
[node name="sc" type="ScrollContainer" parent="layout/RSplit"]
|
|
||||||
visible = false
|
|
||||||
offset_left = 593.0
|
|
||||||
offset_right = 1093.0
|
|
||||||
offset_bottom = 555.0
|
|
||||||
size_flags_vertical = 3
|
|
||||||
|
|
||||||
[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"]
|
|
||||||
offset_right = 500.0
|
|
||||||
offset_bottom = 555.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="CResults" type="VBoxContainer" parent="layout/RSplit"]
|
[node name="CResults" type="VBoxContainer" parent="layout/RSplit"]
|
||||||
offset_right = 1023.0
|
layout_mode = 2
|
||||||
offset_bottom = 564.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="ControlBar" type="HBoxContainer" parent="layout/RSplit/CResults"]
|
[node name="ControlBar" type="HBoxContainer" parent="layout/RSplit/CResults"]
|
||||||
offset_right = 1023.0
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Sep2" type="ColorRect" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Light3D" type="Control" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="Light3D" type="Control" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
visible = false
|
custom_minimum_size = Vector2(30, 30)
|
||||||
offset_right = 30.0
|
layout_mode = 2
|
||||||
offset_bottom = 35.0
|
|
||||||
|
|
||||||
[node name="Passing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="Passing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 34.0
|
layout_mode = 2
|
||||||
offset_right = 107.0
|
|
||||||
offset_bottom = 35.0
|
|
||||||
|
|
||||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
||||||
offset_right = 2.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_bottom = 35.0
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
||||||
offset_left = 6.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 54.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "Passing"
|
text = "Passing"
|
||||||
|
|
||||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
||||||
offset_left = 58.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 73.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "---"
|
text = "---"
|
||||||
|
|
||||||
[node name="Failing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="Failing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 34.0
|
layout_mode = 2
|
||||||
offset_right = 100.0
|
|
||||||
offset_bottom = 35.0
|
|
||||||
|
|
||||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
||||||
offset_right = 2.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_bottom = 35.0
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
||||||
offset_left = 6.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 47.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "Failing"
|
text = "Failing"
|
||||||
|
|
||||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
||||||
offset_left = 51.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 66.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "---"
|
text = "---"
|
||||||
|
|
||||||
[node name="Pending" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="Pending" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 34.0
|
layout_mode = 2
|
||||||
offset_right = 110.0
|
|
||||||
offset_bottom = 35.0
|
|
||||||
|
|
||||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
||||||
offset_right = 2.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_bottom = 35.0
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
||||||
offset_left = 6.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 57.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "Pending"
|
text = "Pending"
|
||||||
|
|
||||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
||||||
offset_left = 61.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 76.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "---"
|
text = "---"
|
||||||
|
|
||||||
[node name="Orphans" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="Orphans" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 34.0
|
layout_mode = 2
|
||||||
offset_right = 110.0
|
|
||||||
offset_bottom = 35.0
|
|
||||||
|
|
||||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
||||||
offset_right = 2.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_bottom = 35.0
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
||||||
offset_left = 6.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 57.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "Orphans"
|
text = "Orphans"
|
||||||
|
|
||||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
||||||
offset_left = 61.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 76.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "---"
|
text = "---"
|
||||||
|
|
||||||
[node name="Errors" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="Errors" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 34.0
|
layout_mode = 2
|
||||||
offset_right = 96.0
|
|
||||||
offset_bottom = 35.0
|
|
||||||
|
|
||||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
||||||
offset_right = 2.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_bottom = 35.0
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
||||||
offset_left = 6.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 43.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "The number of GUT errors generated. This does not include engine errors."
|
|
||||||
text = "Errors"
|
text = "Errors"
|
||||||
|
|
||||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
||||||
offset_left = 47.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 62.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "---"
|
text = "---"
|
||||||
|
|
||||||
[node name="Warnings" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="Warnings" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 34.0
|
layout_mode = 2
|
||||||
offset_right = 118.0
|
|
||||||
offset_bottom = 35.0
|
|
||||||
|
|
||||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
||||||
offset_right = 2.0
|
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||||
offset_bottom = 35.0
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
||||||
offset_left = 6.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 65.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "Warnings"
|
text = "Warnings"
|
||||||
|
|
||||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
||||||
offset_left = 69.0
|
layout_mode = 2
|
||||||
offset_top = 10.0
|
|
||||||
offset_right = 84.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = "---"
|
text = "---"
|
||||||
|
|
||||||
[node name="CenterContainer" type="CenterContainer" parent="layout/RSplit/CResults/ControlBar"]
|
[node name="CenterContainer" type="CenterContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||||
offset_right = 1023.0
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="TabBar" type="HSplitContainer" parent="layout/RSplit/CResults"]
|
[node name="TabBar" type="HSplitContainer" parent="layout/RSplit/CResults"]
|
||||||
offset_top = 4.0
|
layout_mode = 2
|
||||||
offset_right = 1023.0
|
|
||||||
offset_bottom = 564.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="RunResults" parent="layout/RSplit/CResults/TabBar" instance=ExtResource("5")]
|
[node name="RunResults" parent="layout/RSplit/CResults/TabBar" instance=ExtResource("5")]
|
||||||
offset_right = 505.0
|
layout_mode = 2
|
||||||
offset_bottom = 560.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="OutputText" parent="layout/RSplit/CResults/TabBar" instance=ExtResource("6")]
|
[node name="OutputText" parent="layout/RSplit/CResults/TabBar" instance=ExtResource("6")]
|
||||||
offset_left = 517.0
|
visible = false
|
||||||
offset_right = 1023.0
|
layout_mode = 2
|
||||||
offset_bottom = 560.0
|
|
||||||
|
[node name="sc" type="ScrollContainer" parent="layout/RSplit"]
|
||||||
|
custom_minimum_size = Vector2(500, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="BottomPanelShortcuts" parent="." instance=ExtResource("2")]
|
[node name="BottomPanelShortcuts" parent="." instance=ExtResource("2")]
|
||||||
visible = false
|
visible = false
|
||||||
|
|
322
addons/gut/gui/GutControl.gd
Normal file
322
addons/gut/gui/GutControl.gd
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
@tool
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
const RUNNER_JSON_PATH = "res://.gut_editor_config.json"
|
||||||
|
|
||||||
|
var GutConfig = load("res://addons/gut/gut_config.gd")
|
||||||
|
var GutRunnerScene = load("res://addons/gut/gui/GutRunner.tscn")
|
||||||
|
var GutConfigGui = load("res://addons/gut/gui/gut_config_gui.gd")
|
||||||
|
|
||||||
|
var _config = GutConfig.new()
|
||||||
|
var _config_gui = null
|
||||||
|
var _gut_runner = GutRunnerScene.instantiate()
|
||||||
|
var _has_connected = false
|
||||||
|
var _tree_root: TreeItem = null
|
||||||
|
|
||||||
|
var _script_icon = load("res://addons/gut/images/Script.svg")
|
||||||
|
var _folder_icon = load("res://addons/gut/images/Folder.svg")
|
||||||
|
|
||||||
|
var _tree_scripts = {}
|
||||||
|
var _tree_directories = {}
|
||||||
|
|
||||||
|
const TREE_SCRIPT = "Script"
|
||||||
|
const TREE_DIR = "Directory"
|
||||||
|
|
||||||
|
@onready var _ctrls = {
|
||||||
|
run_tests_button = $VBox/Buttons/RunTests,
|
||||||
|
run_selected = $VBox/Buttons/RunSelected,
|
||||||
|
test_tree = $VBox/Tabs/Tests,
|
||||||
|
settings_vbox = $VBox/Tabs/SettingsScroll/Settings,
|
||||||
|
tabs = $VBox/Tabs,
|
||||||
|
bg = $Bg
|
||||||
|
}
|
||||||
|
|
||||||
|
@export var bg_color: Color = Color(.36, .36, .36):
|
||||||
|
get:
|
||||||
|
return bg_color
|
||||||
|
set(val):
|
||||||
|
bg_color = val
|
||||||
|
if is_inside_tree():
|
||||||
|
$Bg.color = bg_color
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
if Engine.is_editor_hint():
|
||||||
|
return
|
||||||
|
|
||||||
|
$Bg.color = bg_color
|
||||||
|
_ctrls.tabs.set_tab_title(0, "Tests")
|
||||||
|
_ctrls.tabs.set_tab_title(1, "Settings")
|
||||||
|
|
||||||
|
_config_gui = GutConfigGui.new(_ctrls.settings_vbox)
|
||||||
|
|
||||||
|
_ctrls.test_tree.hide_root = true
|
||||||
|
# Stop tests from kicking off when the runner is "ready" and
|
||||||
|
# prevents it from writing results file that is used by
|
||||||
|
# the panel.
|
||||||
|
_gut_runner.set_cmdln_mode(true)
|
||||||
|
add_child(_gut_runner)
|
||||||
|
|
||||||
|
# Becuase of the janky _utils psuedo-global script, we cannot
|
||||||
|
# do all this in _ready. If we do this in _ready, it generates
|
||||||
|
# a bunch of errors. The errors don't matter, but it looks bad.
|
||||||
|
call_deferred("_post_ready")
|
||||||
|
|
||||||
|
|
||||||
|
func _draw():
|
||||||
|
if Engine.is_editor_hint():
|
||||||
|
return
|
||||||
|
|
||||||
|
var gut = _gut_runner.get_gut()
|
||||||
|
if !gut.is_running():
|
||||||
|
var r = Rect2(Vector2(0, 0), get_rect().size)
|
||||||
|
draw_rect(r, Color.BLACK, false, 2)
|
||||||
|
|
||||||
|
|
||||||
|
func _post_ready():
|
||||||
|
var gut = _gut_runner.get_gut()
|
||||||
|
gut.start_run.connect(_on_gut_run_started)
|
||||||
|
gut.end_run.connect(_on_gut_run_ended)
|
||||||
|
_refresh_tree_and_settings()
|
||||||
|
|
||||||
|
|
||||||
|
func _set_meta_for_script_tree_item(item, script, test = null):
|
||||||
|
var meta = {
|
||||||
|
type = TREE_SCRIPT, script = script.path, inner_class = script.inner_class_name, test = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if test != null:
|
||||||
|
meta.test = test.name
|
||||||
|
|
||||||
|
item.set_metadata(0, meta)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_meta_for_directory_tree_item(item, path, temp_item):
|
||||||
|
var meta = {type = TREE_DIR, path = path, temp_item = temp_item}
|
||||||
|
item.set_metadata(0, meta)
|
||||||
|
|
||||||
|
|
||||||
|
func _get_script_tree_item(script, parent_item):
|
||||||
|
if !_tree_scripts.has(script.path):
|
||||||
|
var item = _ctrls.test_tree.create_item(parent_item)
|
||||||
|
item.set_text(0, script.path.get_file())
|
||||||
|
item.set_icon(0, _script_icon)
|
||||||
|
_tree_scripts[script.path] = item
|
||||||
|
_set_meta_for_script_tree_item(item, script)
|
||||||
|
|
||||||
|
return _tree_scripts[script.path]
|
||||||
|
|
||||||
|
|
||||||
|
func _get_directory_tree_item(path):
|
||||||
|
var parent = _tree_root
|
||||||
|
if !_tree_directories.has(path):
|
||||||
|
var item: TreeItem = null
|
||||||
|
if parent != _tree_root:
|
||||||
|
item = parent.create_child(0)
|
||||||
|
else:
|
||||||
|
item = parent.create_child()
|
||||||
|
|
||||||
|
_tree_directories[path] = item
|
||||||
|
item.collapsed = false
|
||||||
|
item.set_text(0, path)
|
||||||
|
item.set_icon(0, _folder_icon)
|
||||||
|
item.set_icon_modulate(0, Color.ROYAL_BLUE)
|
||||||
|
# temp_item is used in calls with move_before since you must use
|
||||||
|
# move_before or move_after to reparent tree items. This ensures that
|
||||||
|
# there is an item on all directories. These are deleted later.
|
||||||
|
var temp_item = item.create_child()
|
||||||
|
temp_item.set_text(0, "<temp>")
|
||||||
|
|
||||||
|
_set_meta_for_directory_tree_item(item, path, temp_item)
|
||||||
|
|
||||||
|
return _tree_directories[path]
|
||||||
|
|
||||||
|
|
||||||
|
func _find_dir_item_to_move_before(path):
|
||||||
|
var max_matching_len = 0
|
||||||
|
var best_parent = null
|
||||||
|
|
||||||
|
# Go through all the directory items finding the one that has the longest
|
||||||
|
# path that contains our path.
|
||||||
|
for key in _tree_directories.keys():
|
||||||
|
if path != key and path.begins_with(key) and key.length() > max_matching_len:
|
||||||
|
max_matching_len = key.length()
|
||||||
|
best_parent = _tree_directories[key]
|
||||||
|
|
||||||
|
var to_return = null
|
||||||
|
if best_parent != null:
|
||||||
|
to_return = best_parent.get_metadata(0).temp_item
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
func _reorder_dir_items():
|
||||||
|
var the_keys = _tree_directories.keys()
|
||||||
|
the_keys.sort()
|
||||||
|
for key in _tree_directories.keys():
|
||||||
|
var to_move = _tree_directories[key]
|
||||||
|
to_move.collapsed = false
|
||||||
|
var move_before = _find_dir_item_to_move_before(key)
|
||||||
|
if move_before != null:
|
||||||
|
to_move.move_before(move_before)
|
||||||
|
var new_text = key.substr(move_before.get_parent().get_metadata(0).path.length())
|
||||||
|
to_move.set_text(0, new_text)
|
||||||
|
|
||||||
|
|
||||||
|
func _remove_dir_temp_items():
|
||||||
|
for key in _tree_directories.keys():
|
||||||
|
var item = _tree_directories[key].get_metadata(0).temp_item
|
||||||
|
_tree_directories[key].remove_child(item)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_dir_and_script_tree_items():
|
||||||
|
var tree: Tree = _ctrls.test_tree
|
||||||
|
tree.clear()
|
||||||
|
_tree_root = _ctrls.test_tree.create_item()
|
||||||
|
|
||||||
|
var scripts = _gut_runner.get_gut().get_test_collector().scripts
|
||||||
|
for script in scripts:
|
||||||
|
var dir_item = _get_directory_tree_item(script.path.get_base_dir())
|
||||||
|
var item = _get_script_tree_item(script, dir_item)
|
||||||
|
|
||||||
|
if script.inner_class_name != "":
|
||||||
|
var inner_item = tree.create_item(item)
|
||||||
|
inner_item.set_text(0, script.inner_class_name)
|
||||||
|
_set_meta_for_script_tree_item(inner_item, script)
|
||||||
|
item = inner_item
|
||||||
|
|
||||||
|
for test in script.tests:
|
||||||
|
var test_item = tree.create_item(item)
|
||||||
|
test_item.set_text(0, test.name)
|
||||||
|
_set_meta_for_script_tree_item(test_item, script, test)
|
||||||
|
|
||||||
|
|
||||||
|
func _populate_tree():
|
||||||
|
_add_dir_and_script_tree_items()
|
||||||
|
_tree_root.set_collapsed_recursive(true)
|
||||||
|
_tree_root.set_collapsed(false)
|
||||||
|
_reorder_dir_items()
|
||||||
|
_remove_dir_temp_items()
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh_tree_and_settings():
|
||||||
|
if _config.options.has("panel_options"):
|
||||||
|
_config_gui.set_options(_config.options)
|
||||||
|
_config.apply_options(_gut_runner.get_gut())
|
||||||
|
_gut_runner.set_gut_config(_config)
|
||||||
|
_populate_tree()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------
|
||||||
|
# Events
|
||||||
|
# ---------------------------
|
||||||
|
func _on_gut_run_started():
|
||||||
|
_ctrls.run_tests_button.disabled = true
|
||||||
|
_ctrls.run_selected.visible = false
|
||||||
|
_ctrls.tabs.visible = false
|
||||||
|
_ctrls.bg.visible = false
|
||||||
|
_ctrls.run_tests_button.text = "Running"
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_run_ended():
|
||||||
|
_ctrls.run_tests_button.disabled = false
|
||||||
|
_ctrls.run_selected.visible = true
|
||||||
|
_ctrls.tabs.visible = true
|
||||||
|
_ctrls.bg.visible = true
|
||||||
|
_ctrls.run_tests_button.text = "Run All"
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_run_tests_pressed():
|
||||||
|
run_all()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_run_selected_pressed():
|
||||||
|
run_selected()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_tests_item_activated():
|
||||||
|
run_selected()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------
|
||||||
|
# Public
|
||||||
|
# ---------------------------
|
||||||
|
func get_gut():
|
||||||
|
return _gut_runner.get_gut()
|
||||||
|
|
||||||
|
|
||||||
|
func get_config():
|
||||||
|
return _config
|
||||||
|
|
||||||
|
|
||||||
|
func run_all():
|
||||||
|
_config.options.selected = ""
|
||||||
|
_config.options.inner_class_name = ""
|
||||||
|
_config.options.unit_test_name = ""
|
||||||
|
run_tests()
|
||||||
|
|
||||||
|
|
||||||
|
func run_tests(options = null):
|
||||||
|
if options == null:
|
||||||
|
_config.options = _config_gui.get_options(_config.options)
|
||||||
|
else:
|
||||||
|
_config.options = options
|
||||||
|
|
||||||
|
_gut_runner.get_gut().get_test_collector().clear()
|
||||||
|
_gut_runner.set_gut_config(_config)
|
||||||
|
_gut_runner.run_tests()
|
||||||
|
|
||||||
|
|
||||||
|
func run_selected():
|
||||||
|
var sel_item = _ctrls.test_tree.get_selected()
|
||||||
|
if sel_item == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
var options = _config_gui.get_options(_config.options)
|
||||||
|
var meta = sel_item.get_metadata(0)
|
||||||
|
if meta.type == TREE_SCRIPT:
|
||||||
|
options.selected = meta.script.get_file()
|
||||||
|
options.inner_class_name = meta.inner_class
|
||||||
|
options.unit_test_name = meta.test
|
||||||
|
elif meta.type == TREE_DIR:
|
||||||
|
options.dirs = [meta.path]
|
||||||
|
options.include_subdirectories = true
|
||||||
|
options.selected = ""
|
||||||
|
options.inner_class_name = ""
|
||||||
|
options.unit_test_name = ""
|
||||||
|
|
||||||
|
run_tests(options)
|
||||||
|
|
||||||
|
|
||||||
|
func load_config_file(path):
|
||||||
|
_config.load_panel_options(path)
|
||||||
|
_config.options.selected = ""
|
||||||
|
_config.options.inner_class_name = ""
|
||||||
|
_config.options.unit_test_name = ""
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
# The MIT License (MIT)
|
||||||
|
# =====================
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 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.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
63
addons/gut/gui/GutControl.tscn
Normal file
63
addons/gut/gui/GutControl.tscn
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://4jb53yqktyfg"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/gut/gui/GutControl.gd" id="1_eprql"]
|
||||||
|
|
||||||
|
[node name="GutControl" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 0
|
||||||
|
offset_right = 295.0
|
||||||
|
offset_bottom = 419.0
|
||||||
|
script = ExtResource("1_eprql")
|
||||||
|
|
||||||
|
[node name="Bg" type="ColorRect" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
color = Color(0.36, 0.36, 0.36, 1)
|
||||||
|
|
||||||
|
[node name="VBox" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Tabs" type="TabContainer" parent="VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Tests" type="Tree" parent="VBox/Tabs"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
hide_root = true
|
||||||
|
|
||||||
|
[node name="SettingsScroll" type="ScrollContainer" parent="VBox/Tabs"]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Settings" type="VBoxContainer" parent="VBox/Tabs/SettingsScroll"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Buttons" type="HBoxContainer" parent="VBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="RunTests" type="Button" parent="VBox/Buttons"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Run All"
|
||||||
|
|
||||||
|
[node name="RunSelected" type="Button" parent="VBox/Buttons"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Run Selected"
|
||||||
|
|
||||||
|
[connection signal="item_activated" from="VBox/Tabs/Tests" to="." method="_on_tests_item_activated"]
|
||||||
|
[connection signal="pressed" from="VBox/Buttons/RunTests" to="." method="_on_run_tests_pressed"]
|
||||||
|
[connection signal="pressed" from="VBox/Buttons/RunSelected" to="." method="_on_run_selected_pressed"]
|
|
@ -1,3 +1,18 @@
|
||||||
|
# ##############################################################################
|
||||||
|
# This class joins together GUT, GUT Gui, GutConfig and is the main way to
|
||||||
|
# run a test suite.
|
||||||
|
#
|
||||||
|
# This creates its own instance of gut.gd that it manages.
|
||||||
|
# Use set_gut_config to set the gut_config.gd that should be used to configure
|
||||||
|
# gut.
|
||||||
|
# This will create a GUI and wire it up and apply gut_config.gd options.
|
||||||
|
#
|
||||||
|
# Running tests:
|
||||||
|
# By default, this will run tests once this control has been added to the tree.
|
||||||
|
# You can override this by setting auto_run_tests to false prior to adding
|
||||||
|
# this to the tree. To run tests manually, call run_tests.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
var Gut = load("res://addons/gut/gut.gd")
|
var Gut = load("res://addons/gut/gut.gd")
|
||||||
|
@ -19,6 +34,7 @@ var _cmdln_mode = false
|
||||||
@onready var _gut_layer = $GutLayer
|
@onready var _gut_layer = $GutLayer
|
||||||
@onready var _gui = $GutLayer/GutScene
|
@onready var _gui = $GutLayer/GutScene
|
||||||
|
|
||||||
|
# When true, tests will be kicked off in _ready.
|
||||||
var auto_run_tests = true
|
var auto_run_tests = true
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,29 +50,9 @@ func _ready():
|
||||||
call_deferred("run_tests")
|
call_deferred("run_tests")
|
||||||
|
|
||||||
|
|
||||||
func run_tests(show_gui = true):
|
func _lazy_make_gut():
|
||||||
if _gut == null:
|
if _gut == null:
|
||||||
get_gut()
|
_gut = Gut.new()
|
||||||
|
|
||||||
_setup_gui(show_gui)
|
|
||||||
|
|
||||||
_gut.add_children_to = self
|
|
||||||
if _gut_config.options.gut_on_top:
|
|
||||||
_gut_layer.add_child(_gut)
|
|
||||||
else:
|
|
||||||
add_child(_gut)
|
|
||||||
|
|
||||||
if !_cmdln_mode:
|
|
||||||
_gut.end_run.connect(
|
|
||||||
_on_tests_finished.bind(
|
|
||||||
_gut_config.options.should_exit, _gut_config.options.should_exit_on_success
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
_gut_config.config_gut(_gut)
|
|
||||||
var run_rest_of_scripts = _gut_config.options.unit_test_name == ""
|
|
||||||
|
|
||||||
_gut.test_scripts(run_rest_of_scripts)
|
|
||||||
|
|
||||||
|
|
||||||
func _setup_gui(show_gui):
|
func _setup_gui(show_gui):
|
||||||
|
@ -76,20 +72,18 @@ func _setup_gui(show_gui):
|
||||||
if opts.background_color != null and opts.background_color.is_valid_html_color():
|
if opts.background_color != null and opts.background_color.is_valid_html_color():
|
||||||
_gui.set_background_color(Color(opts.background_color))
|
_gui.set_background_color(Color(opts.background_color))
|
||||||
|
|
||||||
#_tester.set_modulate(Color(1.0, 1.0, 1.0, min(1.0, float(opts.opacity) / 100)))
|
_gui.set_opacity(min(1.0, float(opts.opacity) / 100))
|
||||||
# if(opts.should_maximize):
|
# if(opts.should_maximize):
|
||||||
# _tester.maximize()
|
# _tester.maximize()
|
||||||
#if(opts.compact_mode):
|
_gui.use_compact_mode(opts.compact_mode)
|
||||||
# _tester.get_gui().compact_mode(true)
|
|
||||||
|
|
||||||
|
|
||||||
func _write_results():
|
func _write_results():
|
||||||
var content = _gui.get_textbox().text #_gut.logger.get_gui_bbcode()
|
var content = _gui.get_textbox().get_parsed_text() #_gut.logger.get_gui_bbcode()
|
||||||
|
|
||||||
var f = FileAccess.open(RESULT_FILE, FileAccess.WRITE)
|
var f = FileAccess.open(RESULT_FILE, FileAccess.WRITE)
|
||||||
if f != null:
|
if f != null:
|
||||||
f.store_string(content)
|
f.store_string(content)
|
||||||
f.close()
|
f = null # closes file
|
||||||
else:
|
else:
|
||||||
push_error("Could not save bbcode, result = ", FileAccess.get_open_error())
|
push_error("Could not save bbcode, result = ", FileAccess.get_open_error())
|
||||||
|
|
||||||
|
@ -112,9 +106,33 @@ func _on_tests_finished(should_exit, should_exit_on_success):
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
|
func run_tests(show_gui = true):
|
||||||
|
_lazy_make_gut()
|
||||||
|
|
||||||
|
_setup_gui(show_gui)
|
||||||
|
|
||||||
|
_gut.add_children_to = self
|
||||||
|
if _gut.get_parent() == null:
|
||||||
|
if _gut_config.options.gut_on_top:
|
||||||
|
_gut_layer.add_child(_gut)
|
||||||
|
else:
|
||||||
|
add_child(_gut)
|
||||||
|
|
||||||
|
if !_cmdln_mode:
|
||||||
|
_gut.end_run.connect(
|
||||||
|
_on_tests_finished.bind(
|
||||||
|
_gut_config.options.should_exit, _gut_config.options.should_exit_on_success
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
_gut_config.apply_options(_gut)
|
||||||
|
var run_rest_of_scripts = _gut_config.options.unit_test_name == ""
|
||||||
|
|
||||||
|
_gut.test_scripts(run_rest_of_scripts)
|
||||||
|
|
||||||
|
|
||||||
func get_gut():
|
func get_gut():
|
||||||
if _gut == null:
|
_lazy_make_gut()
|
||||||
_gut = Gut.new()
|
|
||||||
return _gut
|
return _gut
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,3 +142,29 @@ func set_gut_config(which):
|
||||||
|
|
||||||
func set_cmdln_mode(is_it):
|
func set_cmdln_mode(is_it):
|
||||||
_cmdln_mode = is_it
|
_cmdln_mode = is_it
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
# The MIT License (MIT)
|
||||||
|
# =====================
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 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.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
|
|
File diff suppressed because one or more lines are too long
161
addons/gut/gui/MinGui.tscn
Normal file
161
addons/gut/gui/MinGui.tscn
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
[gd_scene load_steps=5 format=3 uid="uid://cnqqdfsn80ise"]
|
||||||
|
|
||||||
|
[ext_resource type="Theme" uid="uid://cstkhwkpajvqu" path="res://addons/gut/gui/GutSceneTheme.tres" id="1_farmq"]
|
||||||
|
[ext_resource type="FontFile" uid="uid://bnh0lslf4yh87" path="res://addons/gut/fonts/CourierPrime-Regular.ttf" id="2_a2e2l"]
|
||||||
|
[ext_resource type="Script" path="res://addons/gut/gui/gut_gui.gd" id="2_eokrf"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bvrqqgjpyouse" path="res://addons/gut/gui/ResizeHandle.tscn" id="4_xrhva"]
|
||||||
|
|
||||||
|
[node name="Min" type="Panel"]
|
||||||
|
clip_contents = true
|
||||||
|
custom_minimum_size = Vector2(280, 145)
|
||||||
|
offset_right = 280.0
|
||||||
|
offset_bottom = 145.0
|
||||||
|
theme = ExtResource("1_farmq")
|
||||||
|
script = ExtResource("2_eokrf")
|
||||||
|
|
||||||
|
[node name="MainBox" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
metadata/_edit_layout_mode = 1
|
||||||
|
|
||||||
|
[node name="TitleBar" type="Panel" parent="MainBox"]
|
||||||
|
custom_minimum_size = Vector2(0, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="TitleBox" type="HBoxContainer" parent="MainBox/TitleBar"]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = 2.0
|
||||||
|
offset_bottom = 3.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
metadata/_edit_layout_mode = 1
|
||||||
|
|
||||||
|
[node name="Spacer1" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Title"
|
||||||
|
|
||||||
|
[node name="Spacer2" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="TimeLabel" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "0.000s"
|
||||||
|
|
||||||
|
[node name="Body" type="HBoxContainer" parent="MainBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="LeftMargin" type="CenterContainer" parent="MainBox/Body"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="BodyRows" type="VBoxContainer" parent="MainBox/Body"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="ProgressBars" type="HBoxContainer" parent="MainBox/Body/BodyRows"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/Body/BodyRows/ProgressBars"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "T:"
|
||||||
|
|
||||||
|
[node name="ProgressTest" type="ProgressBar" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(100, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
value = 25.0
|
||||||
|
|
||||||
|
[node name="HBoxContainer2" type="HBoxContainer" parent="MainBox/Body/BodyRows/ProgressBars"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "S:"
|
||||||
|
|
||||||
|
[node name="ProgressScript" type="ProgressBar" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer2"]
|
||||||
|
custom_minimum_size = Vector2(100, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
value = 75.0
|
||||||
|
|
||||||
|
[node name="PathDisplay" type="VBoxContainer" parent="MainBox/Body/BodyRows"]
|
||||||
|
clip_contents = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Path" type="Label" parent="MainBox/Body/BodyRows/PathDisplay"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_fonts/font = ExtResource("2_a2e2l")
|
||||||
|
theme_override_font_sizes/font_size = 14
|
||||||
|
text = "res://test/integration/whatever"
|
||||||
|
clip_text = true
|
||||||
|
text_overrun_behavior = 3
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/Body/BodyRows/PathDisplay"]
|
||||||
|
clip_contents = true
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="S3" type="CenterContainer" parent="MainBox/Body/BodyRows/PathDisplay/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="File" type="Label" parent="MainBox/Body/BodyRows/PathDisplay/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_fonts/font = ExtResource("2_a2e2l")
|
||||||
|
theme_override_font_sizes/font_size = 14
|
||||||
|
text = "test_this_thing.gd"
|
||||||
|
text_overrun_behavior = 3
|
||||||
|
|
||||||
|
[node name="Footer" type="HBoxContainer" parent="MainBox/Body/BodyRows"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="HandleLeft" parent="MainBox/Body/BodyRows/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_xrhva")]
|
||||||
|
layout_mode = 2
|
||||||
|
orientation = 0
|
||||||
|
resize_control = NodePath("../../../../..")
|
||||||
|
vertical_resize = false
|
||||||
|
|
||||||
|
[node name="SwitchModes" type="Button" parent="MainBox/Body/BodyRows/Footer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Expand"
|
||||||
|
|
||||||
|
[node name="CenterContainer" type="CenterContainer" parent="MainBox/Body/BodyRows/Footer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="Continue" type="Button" parent="MainBox/Body/BodyRows/Footer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Continue
|
||||||
|
"
|
||||||
|
|
||||||
|
[node name="HandleRight" parent="MainBox/Body/BodyRows/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_xrhva")]
|
||||||
|
layout_mode = 2
|
||||||
|
resize_control = NodePath("../../../../..")
|
||||||
|
vertical_resize = false
|
||||||
|
|
||||||
|
[node name="RightMargin" type="CenterContainer" parent="MainBox/Body"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="CenterContainer" type="CenterContainer" parent="MainBox"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2)
|
||||||
|
layout_mode = 2
|
213
addons/gut/gui/NormalGui.tscn
Normal file
213
addons/gut/gui/NormalGui.tscn
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
[gd_scene load_steps=5 format=3 uid="uid://duxblir3vu8x7"]
|
||||||
|
|
||||||
|
[ext_resource type="Theme" uid="uid://cstkhwkpajvqu" path="res://addons/gut/gui/GutSceneTheme.tres" id="1_5hlsm"]
|
||||||
|
[ext_resource type="Script" path="res://addons/gut/gui/gut_gui.gd" id="2_fue6q"]
|
||||||
|
[ext_resource type="FontFile" uid="uid://bnh0lslf4yh87" path="res://addons/gut/fonts/CourierPrime-Regular.ttf" id="2_u5uc1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bvrqqgjpyouse" path="res://addons/gut/gui/ResizeHandle.tscn" id="4_2r8a8"]
|
||||||
|
|
||||||
|
[node name="Large" type="Panel"]
|
||||||
|
custom_minimum_size = Vector2(500, 150)
|
||||||
|
offset_right = 632.0
|
||||||
|
offset_bottom = 260.0
|
||||||
|
theme = ExtResource("1_5hlsm")
|
||||||
|
script = ExtResource("2_fue6q")
|
||||||
|
|
||||||
|
[node name="MainBox" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
metadata/_edit_layout_mode = 1
|
||||||
|
|
||||||
|
[node name="TitleBar" type="Panel" parent="MainBox"]
|
||||||
|
custom_minimum_size = Vector2(0, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="TitleBox" type="HBoxContainer" parent="MainBox/TitleBar"]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = 2.0
|
||||||
|
offset_bottom = 3.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
metadata/_edit_layout_mode = 1
|
||||||
|
|
||||||
|
[node name="Spacer1" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Title"
|
||||||
|
|
||||||
|
[node name="Spacer2" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="TimeLabel" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||||
|
custom_minimum_size = Vector2(90, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
text = "999.999s"
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="MainBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="OutputBG" type="ColorRect" parent="MainBox/HBoxContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
color = Color(0.0745098, 0.0705882, 0.0784314, 1)
|
||||||
|
metadata/_edit_layout_mode = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG"]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="S2" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="TestOutput" type="RichTextLabel" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
focus_mode = 2
|
||||||
|
bbcode_enabled = true
|
||||||
|
scroll_following = true
|
||||||
|
autowrap_mode = 0
|
||||||
|
selection_enabled = true
|
||||||
|
|
||||||
|
[node name="S1" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ControlBox" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="S1" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ProgressBars" type="VBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="TestBox" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/TestBox"]
|
||||||
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Tests"
|
||||||
|
|
||||||
|
[node name="ProgressTest" type="ProgressBar" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/TestBox"]
|
||||||
|
custom_minimum_size = Vector2(100, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
value = 25.0
|
||||||
|
|
||||||
|
[node name="ScriptBox" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/ScriptBox"]
|
||||||
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Scripts"
|
||||||
|
|
||||||
|
[node name="ProgressScript" type="ProgressBar" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/ScriptBox"]
|
||||||
|
custom_minimum_size = Vector2(100, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
value = 75.0
|
||||||
|
|
||||||
|
[node name="PathDisplay" type="VBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Path" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 6
|
||||||
|
theme_override_fonts/font = ExtResource("2_u5uc1")
|
||||||
|
theme_override_font_sizes/font_size = 14
|
||||||
|
text = "res://test/integration/whatever"
|
||||||
|
text_overrun_behavior = 3
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="S3" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="File" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_fonts/font = ExtResource("2_u5uc1")
|
||||||
|
theme_override_font_sizes/font_size = 14
|
||||||
|
text = "test_this_thing.gd"
|
||||||
|
text_overrun_behavior = 3
|
||||||
|
|
||||||
|
[node name="Spacer1" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 10
|
||||||
|
|
||||||
|
[node name="Continue" type="Button" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
text = "Continue
|
||||||
|
"
|
||||||
|
|
||||||
|
[node name="S3" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||||
|
custom_minimum_size = Vector2(5, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="BottomPad" type="CenterContainer" parent="MainBox"]
|
||||||
|
custom_minimum_size = Vector2(0, 5)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Footer" type="HBoxContainer" parent="MainBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="SidePad1" type="CenterContainer" parent="MainBox/Footer"]
|
||||||
|
custom_minimum_size = Vector2(2, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ResizeHandle3" parent="MainBox/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_2r8a8")]
|
||||||
|
custom_minimum_size = Vector2(25, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
orientation = 0
|
||||||
|
resize_control = NodePath("../../..")
|
||||||
|
|
||||||
|
[node name="SwitchModes" type="Button" parent="MainBox/Footer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Compact
|
||||||
|
"
|
||||||
|
|
||||||
|
[node name="CenterContainer" type="CenterContainer" parent="MainBox/Footer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="ResizeHandle2" parent="MainBox/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_2r8a8")]
|
||||||
|
custom_minimum_size = Vector2(25, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
resize_control = NodePath("../../..")
|
||||||
|
|
||||||
|
[node name="SidePad2" type="CenterContainer" parent="MainBox/Footer"]
|
||||||
|
custom_minimum_size = Vector2(2, 2.08165e-12)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="BottomPad2" type="CenterContainer" parent="MainBox"]
|
||||||
|
custom_minimum_size = Vector2(2.08165e-12, 2)
|
||||||
|
layout_mode = 2
|
|
@ -1,91 +1,64 @@
|
||||||
extends VBoxContainer
|
|
||||||
@tool
|
@tool
|
||||||
|
extends VBoxContainer
|
||||||
|
|
||||||
|
|
||||||
class SearchResults:
|
# ##############################################################################
|
||||||
var L = 0
|
# Keeps search results from the TextEdit
|
||||||
var C = 0
|
# ##############################################################################
|
||||||
|
class TextEditSearcher:
|
||||||
var positions = []
|
var te: TextEdit
|
||||||
var te = null
|
|
||||||
var _last_term = ""
|
var _last_term = ""
|
||||||
|
var _last_pos = Vector2(-1, -1)
|
||||||
|
var _ignore_caret_change = false
|
||||||
|
|
||||||
func _search_te(text, start_position, flags = 0):
|
func set_text_edit(which):
|
||||||
var start_pos = start_position
|
te = which
|
||||||
if start_pos[L] < 0 or start_pos[L] > te.get_line_count():
|
te.caret_changed.connect(_on_caret_changed)
|
||||||
start_pos[L] = 0
|
|
||||||
if start_pos[C] < 0:
|
|
||||||
start_pos[L] = 0
|
|
||||||
|
|
||||||
var result = te.search(text, flags, start_pos[L], start_pos[C])
|
func _on_caret_changed():
|
||||||
if (
|
if _ignore_caret_change:
|
||||||
result.size() == 2
|
_ignore_caret_change = false
|
||||||
and result[L] == start_position[L]
|
|
||||||
and result[C] == start_position[C]
|
|
||||||
and text == _last_term
|
|
||||||
):
|
|
||||||
if flags == TextEdit.SEARCH_BACKWARDS:
|
|
||||||
result[C] -= 1
|
|
||||||
else:
|
else:
|
||||||
result[C] += 1
|
_last_pos = _get_caret()
|
||||||
result = _search_te(text, result, flags)
|
|
||||||
L = result.y
|
|
||||||
C = result.x
|
|
||||||
elif result.size() == 2:
|
|
||||||
te.scroll_vertical = result[L]
|
|
||||||
te.select(result[L], result[C], result[L], result[C] + text.length())
|
|
||||||
te.set_caret_column(result[C])
|
|
||||||
te.set_caret_line(result[L])
|
|
||||||
te.center_viewport_to_caret()
|
|
||||||
L = result.y
|
|
||||||
C = result.x
|
|
||||||
|
|
||||||
_last_term = text
|
func _get_caret():
|
||||||
te.center_viewport_to_caret()
|
return Vector2(te.get_caret_column(), te.get_caret_line())
|
||||||
return result
|
|
||||||
|
|
||||||
func _cursor_to_pos():
|
func _set_caret_and_sel(pos, len):
|
||||||
var to_return = [0, 0]
|
te.set_caret_line(pos.y)
|
||||||
to_return[L] = te.get_caret_line()
|
te.set_caret_column(pos.x)
|
||||||
to_return[C] = te.get_caret_column()
|
if len > 0:
|
||||||
return to_return
|
te.select(pos.y, pos.x, pos.y, pos.x + len)
|
||||||
|
|
||||||
|
func _find(term, search_flags):
|
||||||
|
var pos = _get_caret()
|
||||||
|
if term == _last_term:
|
||||||
|
if search_flags == 0:
|
||||||
|
pos = _last_pos
|
||||||
|
pos.x += 1
|
||||||
|
else:
|
||||||
|
pos = _last_pos
|
||||||
|
pos.x -= 1
|
||||||
|
|
||||||
|
var result = te.search(term, search_flags, pos.y, pos.x)
|
||||||
|
# print('searching from ', pos, ' for "', term, '" = ', result)
|
||||||
|
if result.y != -1:
|
||||||
|
_ignore_caret_change = true
|
||||||
|
_set_caret_and_sel(result, term.length())
|
||||||
|
_last_pos = result
|
||||||
|
|
||||||
|
_last_term = term
|
||||||
|
|
||||||
func find_next(term):
|
func find_next(term):
|
||||||
return _search_te(term, _cursor_to_pos())
|
_find(term, 0)
|
||||||
|
|
||||||
func find_prev(term):
|
func find_prev(term):
|
||||||
var new_pos = _search_te(term, _cursor_to_pos(), TextEdit.SEARCH_BACKWARDS)
|
_find(term, te.SEARCH_BACKWARDS)
|
||||||
return new_pos
|
|
||||||
|
|
||||||
func get_next_pos():
|
|
||||||
pass
|
|
||||||
|
|
||||||
func get_prev_pos():
|
|
||||||
pass
|
|
||||||
|
|
||||||
func clear():
|
|
||||||
pass
|
|
||||||
|
|
||||||
func find_all(text):
|
|
||||||
var c_pos = [0, 0]
|
|
||||||
var found = true
|
|
||||||
var last_pos = [0, 0]
|
|
||||||
positions.clear()
|
|
||||||
|
|
||||||
while found:
|
|
||||||
c_pos = te.search(text, 0, c_pos[L], c_pos[C])
|
|
||||||
|
|
||||||
if (
|
|
||||||
c_pos.size() > 0
|
|
||||||
and (c_pos[L] > last_pos[L] or (c_pos[L] == last_pos[L] and c_pos[C] > last_pos[C]))
|
|
||||||
):
|
|
||||||
positions.append([c_pos[L], c_pos[C]])
|
|
||||||
c_pos[C] += 1
|
|
||||||
last_pos = c_pos
|
|
||||||
else:
|
|
||||||
found = false
|
|
||||||
|
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
# Start OutputText control code
|
||||||
|
# ##############################################################################
|
||||||
@onready var _ctrls = {
|
@onready var _ctrls = {
|
||||||
output = $Output,
|
output = $Output,
|
||||||
copy_button = $Toolbar/CopyButton,
|
copy_button = $Toolbar/CopyButton,
|
||||||
|
@ -93,42 +66,83 @@ class SearchResults:
|
||||||
clear_button = $Toolbar/ClearButton,
|
clear_button = $Toolbar/ClearButton,
|
||||||
word_wrap = $Toolbar/WordWrap,
|
word_wrap = $Toolbar/WordWrap,
|
||||||
show_search = $Toolbar/ShowSearch,
|
show_search = $Toolbar/ShowSearch,
|
||||||
|
caret_position = $Toolbar/LblPosition,
|
||||||
search_bar =
|
search_bar =
|
||||||
{
|
{
|
||||||
bar = $Search,
|
bar = $Search,
|
||||||
search_term = $Search/SearchTerm,
|
search_term = $Search/SearchTerm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var _sr = SearchResults.new()
|
|
||||||
|
var _sr = TextEditSearcher.new()
|
||||||
|
var _highlighter: CodeHighlighter
|
||||||
|
var _font_name = null
|
||||||
|
|
||||||
|
|
||||||
|
# Automatically used when running the OutputText scene from the editor. Changes
|
||||||
|
# to this method only affect test-running the control through the editor.
|
||||||
func _test_running_setup():
|
func _test_running_setup():
|
||||||
_ctrls.use_colors.text = "use colors"
|
_ctrls.use_colors.text = "use colors"
|
||||||
_ctrls.show_search.text = "search"
|
_ctrls.show_search.text = "search"
|
||||||
_ctrls.word_wrap.text = "ww"
|
_ctrls.word_wrap.text = "ww"
|
||||||
|
|
||||||
set_all_fonts("CourierPrime")
|
set_all_fonts("CourierPrime")
|
||||||
set_font_size(20)
|
set_font_size(30)
|
||||||
|
|
||||||
|
_ctrls.output.queue_redraw()
|
||||||
load_file("user://.gut_editor.bbcode")
|
load_file("user://.gut_editor.bbcode")
|
||||||
|
await get_tree().process_frame
|
||||||
|
|
||||||
|
show_search(true)
|
||||||
|
_ctrls.output.set_caret_line(0)
|
||||||
|
_ctrls.output.scroll_vertical = 0
|
||||||
|
_ctrls.output.caret_changed.connect(_on_caret_changed)
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
_sr.te = _ctrls.output
|
_sr.set_text_edit(_ctrls.output)
|
||||||
_ctrls.use_colors.icon = get_theme_icon("RichTextEffect", "EditorIcons")
|
_ctrls.use_colors.icon = get_theme_icon("RichTextEffect", "EditorIcons")
|
||||||
_ctrls.show_search.icon = get_theme_icon("Search", "EditorIcons")
|
_ctrls.show_search.icon = get_theme_icon("Search", "EditorIcons")
|
||||||
_ctrls.word_wrap.icon = get_theme_icon("Loop", "EditorIcons")
|
_ctrls.word_wrap.icon = get_theme_icon("Loop", "EditorIcons")
|
||||||
|
|
||||||
_setup_colors()
|
_setup_colors()
|
||||||
|
_ctrls.use_colors.button_pressed = true
|
||||||
|
_use_highlighting(true)
|
||||||
|
|
||||||
if get_parent() == get_tree().root:
|
if get_parent() == get_tree().root:
|
||||||
_test_running_setup()
|
_test_running_setup()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_caret_changed():
|
||||||
|
var txt = str(
|
||||||
|
"line:", _ctrls.output.get_caret_line(), " col:", _ctrls.output.get_caret_column()
|
||||||
|
)
|
||||||
|
_ctrls.caret_position.text = str(txt)
|
||||||
|
|
||||||
|
|
||||||
# ------------------
|
# ------------------
|
||||||
# Private
|
# Private
|
||||||
# ------------------
|
# ------------------
|
||||||
func _setup_colors():
|
|
||||||
_ctrls.output.clear()
|
|
||||||
|
# Call this after changes in colors and the like to get them to apply. reloads
|
||||||
|
# the text of the output control.
|
||||||
|
func _refresh_output():
|
||||||
|
var orig_pos = _ctrls.output.scroll_vertical
|
||||||
|
var text = _ctrls.output.text
|
||||||
|
|
||||||
|
_ctrls.output.text = text
|
||||||
|
_ctrls.output.scroll_vertical = orig_pos
|
||||||
|
|
||||||
|
|
||||||
|
func _create_highlighter(default_color = Color(1, 1, 1, 1)):
|
||||||
|
var to_return = CodeHighlighter.new()
|
||||||
|
|
||||||
|
to_return.function_color = default_color
|
||||||
|
to_return.number_color = default_color
|
||||||
|
to_return.symbol_color = default_color
|
||||||
|
to_return.member_variable_color = default_color
|
||||||
|
|
||||||
var keywords = [
|
var keywords = [
|
||||||
["Failed", Color.RED],
|
["Failed", Color.RED],
|
||||||
["Passed", Color.GREEN],
|
["Passed", Color.GREEN],
|
||||||
|
@ -139,34 +153,30 @@ func _setup_colors():
|
||||||
]
|
]
|
||||||
|
|
||||||
for keyword in keywords:
|
for keyword in keywords:
|
||||||
if _ctrls.output.syntax_highlighter == null:
|
to_return.add_keyword_color(keyword[0], keyword[1])
|
||||||
_ctrls.output.syntax_highlighter = CodeHighlighter.new()
|
|
||||||
_ctrls.output.syntax_highlighter.add_keyword_color(keyword[0], keyword[1])
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
func _setup_colors():
|
||||||
|
_ctrls.output.clear()
|
||||||
|
|
||||||
var f_color = null
|
var f_color = null
|
||||||
if _ctrls.output.theme == null:
|
if _ctrls.output.theme == null:
|
||||||
f_color = get_theme_color("font_color")
|
f_color = get_theme_color("font_color")
|
||||||
else:
|
else:
|
||||||
f_color = _ctrls.output.theme.font_color
|
f_color = _ctrls.output.theme.font_color
|
||||||
_ctrls.output.add_theme_color_override("font_color_readonly", f_color)
|
|
||||||
_ctrls.output.add_theme_color_override("function_color", f_color)
|
_highlighter = _create_highlighter()
|
||||||
_ctrls.output.add_theme_color_override("member_variable_color", f_color)
|
|
||||||
_ctrls.output.queue_redraw()
|
_ctrls.output.queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
func _set_font(font_name, custom_name):
|
func _use_highlighting(should):
|
||||||
var rtl = _ctrls.output
|
if should:
|
||||||
if font_name == null:
|
_ctrls.output.syntax_highlighter = _highlighter
|
||||||
rtl.set("custom_fonts/" + custom_name, null)
|
|
||||||
else:
|
else:
|
||||||
pass
|
_ctrls.output.syntax_highlighter = null
|
||||||
# cuasing issues in 4.0
|
_refresh_output()
|
||||||
# var dyn_font = FontFile.new()
|
|
||||||
# var font_data = FontFile.new()
|
|
||||||
# font_data.font_path = 'res://addons/gut/fonts/' + font_name + '.ttf'
|
|
||||||
# font_data.antialiased = true
|
|
||||||
# dyn_font.font_data = font_data
|
|
||||||
# rtl.set('custom_fonts/' + custom_name, dyn_font)
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------
|
# ------------------
|
||||||
|
@ -177,7 +187,7 @@ func _on_CopyButton_pressed():
|
||||||
|
|
||||||
|
|
||||||
func _on_UseColors_pressed():
|
func _on_UseColors_pressed():
|
||||||
_ctrls.output.syntax_highlighter = _ctrls.use_colors.button_pressed
|
_use_highlighting(_ctrls.use_colors.button_pressed)
|
||||||
|
|
||||||
|
|
||||||
func _on_ClearButton_pressed():
|
func _on_ClearButton_pressed():
|
||||||
|
@ -215,12 +225,16 @@ func _on_SearchTerm_text_entered(new_text):
|
||||||
|
|
||||||
|
|
||||||
func _on_SearchTerm_gui_input(event):
|
func _on_SearchTerm_gui_input(event):
|
||||||
if event is InputEventKey and !event.pressed and event.scancode == KEY_ESCAPE:
|
if event is InputEventKey and !event.pressed and event.keycode == KEY_ESCAPE:
|
||||||
show_search(false)
|
show_search(false)
|
||||||
|
|
||||||
|
|
||||||
func _on_WordWrap_pressed():
|
func _on_WordWrap_pressed():
|
||||||
_ctrls.output.wrap_enabled = _ctrls.word_wrap.pressed
|
if _ctrls.word_wrap.button_pressed:
|
||||||
|
_ctrls.output.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
|
||||||
|
else:
|
||||||
|
_ctrls.output.wrap_mode = TextEdit.LINE_WRAPPING_NONE
|
||||||
|
|
||||||
_ctrls.output.queue_redraw()
|
_ctrls.output.queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
@ -240,40 +254,57 @@ func search(text, start_pos, highlight = true):
|
||||||
|
|
||||||
|
|
||||||
func copy_to_clipboard():
|
func copy_to_clipboard():
|
||||||
var selected = _ctrls.output.get_selection_text()
|
var selected = _ctrls.output.get_selected_text()
|
||||||
if selected != "":
|
if selected != "":
|
||||||
OS.clipboard = selected
|
DisplayServer.clipboard_set(selected)
|
||||||
else:
|
else:
|
||||||
OS.clipboard = _ctrls.output.text
|
DisplayServer.clipboard_set(_ctrls.output.text)
|
||||||
|
|
||||||
|
|
||||||
func clear():
|
func clear():
|
||||||
_ctrls.output.text = ""
|
_ctrls.output.text = ""
|
||||||
|
|
||||||
|
|
||||||
|
func _set_font(font_name, custom_name):
|
||||||
|
var rtl = _ctrls.output
|
||||||
|
if font_name == null:
|
||||||
|
rtl.add_theme_font_override(custom_name, null)
|
||||||
|
else:
|
||||||
|
var dyn_font = FontFile.new()
|
||||||
|
dyn_font.load_dynamic_font("res://addons/gut/fonts/" + font_name + ".ttf")
|
||||||
|
rtl.add_theme_font_override(custom_name, dyn_font)
|
||||||
|
|
||||||
|
|
||||||
func set_all_fonts(base_name):
|
func set_all_fonts(base_name):
|
||||||
|
_font_name = GutUtils.nvl(base_name, "Default")
|
||||||
|
|
||||||
if base_name == "Default":
|
if base_name == "Default":
|
||||||
_set_font(null, "font")
|
_set_font(null, "font")
|
||||||
# _set_font(null, 'normal_font')
|
_set_font(null, "normal_font")
|
||||||
# _set_font(null, 'bold_font')
|
_set_font(null, "bold_font")
|
||||||
# _set_font(null, 'italics_font')
|
_set_font(null, "italics_font")
|
||||||
# _set_font(null, 'bold_italics_font')
|
_set_font(null, "bold_italics_font")
|
||||||
else:
|
else:
|
||||||
_set_font(base_name + "-Regular", "font")
|
_set_font(base_name + "-Regular", "font")
|
||||||
|
_set_font(base_name + "-Regular", "normal_font")
|
||||||
|
_set_font(base_name + "-Bold", "bold_font")
|
||||||
# _set_font(base_name + '-Regular', 'normal_font')
|
_set_font(base_name + "-Italic", "italics_font")
|
||||||
# _set_font(base_name + '-Bold', 'bold_font')
|
_set_font(base_name + "-BoldItalic", "bold_italics_font")
|
||||||
# _set_font(base_name + '-Italic', 'italics_font')
|
|
||||||
# _set_font(base_name + '-BoldItalic', 'bold_italics_font')
|
|
||||||
|
|
||||||
|
|
||||||
func set_font_size(new_size):
|
func set_font_size(new_size):
|
||||||
var rtl = _ctrls.output
|
var rtl = _ctrls.output
|
||||||
if rtl.get("custom_fonts/font") != null:
|
rtl.set("theme_override_font_sizes/font_size", new_size)
|
||||||
rtl.get("custom_fonts/font").size = new_size
|
|
||||||
|
|
||||||
|
|
||||||
|
# rtl.add_theme_font_size_override("font", new_size)
|
||||||
|
# rtl.add_theme_font_size_override("normal_font", new_size)
|
||||||
|
# rtl.add_theme_font_size_override("bold_font", new_size)
|
||||||
|
# rtl.add_theme_font_size_override("italics_font", new_size)
|
||||||
|
# rtl.add_theme_font_size_override("bold_italics_font", new_size)
|
||||||
|
|
||||||
|
# if(rtl.get('custom_fonts/font') != null):
|
||||||
|
# rtl.get('custom_fonts/font').size = new_size
|
||||||
# rtl.get('custom_fonts/bold_italics_font').size = new_size
|
# rtl.get('custom_fonts/bold_italics_font').size = new_size
|
||||||
# rtl.get('custom_fonts/bold_font').size = new_size
|
# rtl.get('custom_fonts/bold_font').size = new_size
|
||||||
# rtl.get('custom_fonts/italics_font').size = new_size
|
# rtl.get('custom_fonts/italics_font').size = new_size
|
||||||
|
@ -298,7 +329,7 @@ func load_file(path):
|
||||||
return
|
return
|
||||||
|
|
||||||
var t = f.get_as_text()
|
var t = f.get_as_text()
|
||||||
f.close()
|
f = null # closes file
|
||||||
_ctrls.output.text = t
|
_ctrls.output.text = t
|
||||||
_ctrls.output.scroll_vertical = _ctrls.output.get_line_count()
|
_ctrls.output.scroll_vertical = _ctrls.output.get_line_count()
|
||||||
_ctrls.output.set_deferred("scroll_vertical", _ctrls.output.get_line_count())
|
_ctrls.output.set_deferred("scroll_vertical", _ctrls.output.get_line_count())
|
||||||
|
|
|
@ -1,113 +1,107 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=5 format=3 uid="uid://bqmo4dj64c7yl"]
|
||||||
|
|
||||||
[ext_resource path="res://addons/gut/gui/OutputText.gd" type="Script" id=1]
|
[ext_resource type="Script" path="res://addons/gut/gui/OutputText.gd" id="1"]
|
||||||
|
|
||||||
[sub_resource type="Image" id=3]
|
[sub_resource type="Image" id="Image_abbh7"]
|
||||||
data = {
|
data = {
|
||||||
"data": PackedByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
|
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||||
"format": "LumAlpha8",
|
"format": "RGBA8",
|
||||||
"height": 16,
|
"height": 16,
|
||||||
"mipmaps": false,
|
"mipmaps": false,
|
||||||
"width": 16
|
"width": 16
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id=2]
|
[sub_resource type="ImageTexture" id="ImageTexture_x655i"]
|
||||||
flags = 4
|
image = SubResource("Image_abbh7")
|
||||||
flags = 4
|
|
||||||
image = SubResource( 3 )
|
[sub_resource type="CodeHighlighter" id="CodeHighlighter_4pcgd"]
|
||||||
size = Vector2( 16, 16 )
|
number_color = Color(1, 1, 1, 1)
|
||||||
|
symbol_color = Color(1, 1, 1, 1)
|
||||||
|
function_color = Color(1, 1, 1, 1)
|
||||||
|
member_variable_color = Color(1, 1, 1, 1)
|
||||||
|
keyword_colors = {
|
||||||
|
"ERROR": Color(1, 0, 0, 1),
|
||||||
|
"Failed": Color(1, 0, 0, 1),
|
||||||
|
"Orphans": Color(1, 1, 0, 1),
|
||||||
|
"Passed": Color(0, 1, 0, 1),
|
||||||
|
"Pending": Color(1, 1, 0, 1),
|
||||||
|
"WARNING": Color(1, 1, 0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
[node name="OutputText" type="VBoxContainer"]
|
[node name="OutputText" type="VBoxContainer"]
|
||||||
offset_right = 862.0
|
offset_right = 862.0
|
||||||
offset_bottom = 523.0
|
offset_bottom = 523.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
script = ExtResource( 1 )
|
script = ExtResource("1")
|
||||||
|
|
||||||
[node name="Toolbar" type="HBoxContainer" parent="."]
|
[node name="Toolbar" type="HBoxContainer" parent="."]
|
||||||
offset_right = 862.0
|
layout_mode = 2
|
||||||
offset_bottom = 24.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="ShowSearch" type="Button" parent="Toolbar"]
|
[node name="ShowSearch" type="Button" parent="Toolbar"]
|
||||||
offset_right = 28.0
|
layout_mode = 2
|
||||||
offset_bottom = 24.0
|
tooltip_text = "Search"
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
icon = SubResource( 2 )
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="UseColors" type="Button" parent="Toolbar"]
|
[node name="UseColors" type="Button" parent="Toolbar"]
|
||||||
offset_left = 32.0
|
layout_mode = 2
|
||||||
offset_right = 60.0
|
tooltip_text = "Colorized Text"
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Colorize output.
|
|
||||||
It's not the same as everywhere else (long story),
|
|
||||||
but it is better than nothing."
|
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
pressed = true
|
button_pressed = true
|
||||||
icon = SubResource( 2 )
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="WordWrap" type="Button" parent="Toolbar"]
|
[node name="WordWrap" type="Button" parent="Toolbar"]
|
||||||
offset_left = 64.0
|
layout_mode = 2
|
||||||
offset_right = 92.0
|
tooltip_text = "Word Wrap"
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Word wrap"
|
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
icon = SubResource( 2 )
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="CenterContainer" type="CenterContainer" parent="Toolbar"]
|
[node name="CenterContainer" type="CenterContainer" parent="Toolbar"]
|
||||||
offset_left = 96.0
|
layout_mode = 2
|
||||||
offset_right = 743.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="LblPosition" type="Label" parent="Toolbar"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="CopyButton" type="Button" parent="Toolbar"]
|
[node name="CopyButton" type="Button" parent="Toolbar"]
|
||||||
offset_left = 747.0
|
layout_mode = 2
|
||||||
offset_right = 798.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Copy to clipboard"
|
|
||||||
text = " Copy "
|
text = " Copy "
|
||||||
|
|
||||||
[node name="ClearButton" type="Button" parent="Toolbar"]
|
[node name="ClearButton" type="Button" parent="Toolbar"]
|
||||||
offset_left = 802.0
|
layout_mode = 2
|
||||||
offset_right = 862.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
text = " Clear "
|
text = " Clear "
|
||||||
|
|
||||||
[node name="Output" type="TextEdit" parent="."]
|
[node name="Output" type="TextEdit" parent="."]
|
||||||
offset_top = 28.0
|
layout_mode = 2
|
||||||
offset_right = 862.0
|
|
||||||
offset_bottom = 523.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
readonly = true
|
text = "Hello World
|
||||||
|
This is a bunch of text
|
||||||
|
That exists for you to see."
|
||||||
|
deselect_on_focus_loss_enabled = false
|
||||||
|
virtual_keyboard_enabled = false
|
||||||
|
middle_mouse_paste_enabled = false
|
||||||
|
highlight_all_occurrences = true
|
||||||
highlight_current_line = true
|
highlight_current_line = true
|
||||||
syntax_highlighter = true
|
syntax_highlighter = SubResource("CodeHighlighter_4pcgd")
|
||||||
show_line_numbers = true
|
scroll_smooth = true
|
||||||
smooth_scrolling = true
|
|
||||||
|
|
||||||
[node name="Search" type="HBoxContainer" parent="."]
|
[node name="Search" type="HBoxContainer" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
offset_top = 499.0
|
layout_mode = 2
|
||||||
offset_right = 862.0
|
|
||||||
offset_bottom = 523.0
|
|
||||||
|
|
||||||
[node name="SearchTerm" type="LineEdit" parent="Search"]
|
[node name="SearchTerm" type="LineEdit" parent="Search"]
|
||||||
offset_right = 804.0
|
layout_mode = 2
|
||||||
offset_bottom = 24.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="SearchNext" type="Button" parent="Search"]
|
[node name="SearchNext" type="Button" parent="Search"]
|
||||||
offset_left = 808.0
|
layout_mode = 2
|
||||||
offset_right = 862.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Find next (enter)"
|
|
||||||
text = "Next"
|
text = "Next"
|
||||||
|
|
||||||
[node name="SearchPrev" type="Button" parent="Search"]
|
[node name="SearchPrev" type="Button" parent="Search"]
|
||||||
offset_left = 808.0
|
layout_mode = 2
|
||||||
offset_right = 820.0
|
|
||||||
offset_bottom = 20.0
|
|
||||||
hint_tooltip = "Find previous (shift + enter)"
|
|
||||||
text = "Prev"
|
text = "Prev"
|
||||||
|
|
||||||
[connection signal="pressed" from="Toolbar/ShowSearch" to="." method="_on_ShowSearch_pressed"]
|
[connection signal="pressed" from="Toolbar/ShowSearch" to="." method="_on_ShowSearch_pressed"]
|
||||||
|
|
108
addons/gut/gui/ResizeHandle.gd
Normal file
108
addons/gut/gui/ResizeHandle.gd
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
@tool
|
||||||
|
extends ColorRect
|
||||||
|
# #############################################################################
|
||||||
|
# Resize Handle control. Place onto a control. Set the orientation, then
|
||||||
|
# set the control that this should resize. Then you can resize the control
|
||||||
|
# by dragging this thing around. It's pretty neat.
|
||||||
|
# #############################################################################
|
||||||
|
enum ORIENTATION { LEFT, RIGHT }
|
||||||
|
|
||||||
|
@export var orientation := ORIENTATION.RIGHT:
|
||||||
|
get:
|
||||||
|
return orientation
|
||||||
|
set(val):
|
||||||
|
orientation = val
|
||||||
|
queue_redraw()
|
||||||
|
@export var resize_control: Control = null
|
||||||
|
@export var vertical_resize := true
|
||||||
|
|
||||||
|
var _line_width = .5
|
||||||
|
var _line_color = Color(.4, .4, .4)
|
||||||
|
var _active_line_color = Color(.3, .3, .3)
|
||||||
|
var _invalid_line_color = Color(1, 0, 0)
|
||||||
|
|
||||||
|
var _grab_margin = 2
|
||||||
|
var _line_space = 3
|
||||||
|
var _num_lines = 8
|
||||||
|
|
||||||
|
var _mouse_down = false
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
|
||||||
|
|
||||||
|
func _draw():
|
||||||
|
var c = _line_color
|
||||||
|
if resize_control == null:
|
||||||
|
c = _invalid_line_color
|
||||||
|
elif _mouse_down:
|
||||||
|
c = _active_line_color
|
||||||
|
|
||||||
|
if orientation == ORIENTATION.LEFT:
|
||||||
|
_draw_resize_handle_left(c)
|
||||||
|
else:
|
||||||
|
_draw_resize_handle_right(c)
|
||||||
|
|
||||||
|
|
||||||
|
func _gui_input(event):
|
||||||
|
if resize_control == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
if orientation == ORIENTATION.LEFT:
|
||||||
|
_handle_left_input(event)
|
||||||
|
else:
|
||||||
|
_handle_right_input(event)
|
||||||
|
|
||||||
|
|
||||||
|
# Draw the lines in the corner to show where you can
|
||||||
|
# drag to resize the dialog
|
||||||
|
func _draw_resize_handle_right(color):
|
||||||
|
var br = size
|
||||||
|
|
||||||
|
for i in range(_num_lines):
|
||||||
|
var start = br - Vector2(i * _line_space, 0)
|
||||||
|
var end = br - Vector2(0, i * _line_space)
|
||||||
|
draw_line(start, end, color, _line_width, true)
|
||||||
|
|
||||||
|
|
||||||
|
func _draw_resize_handle_left(color):
|
||||||
|
var bl = Vector2(0, size.y)
|
||||||
|
|
||||||
|
for i in range(_num_lines):
|
||||||
|
var start = bl + Vector2(i * _line_space, 0)
|
||||||
|
var end = bl - Vector2(0, i * _line_space)
|
||||||
|
draw_line(start, end, color, _line_width, true)
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_right_input(event: InputEvent):
|
||||||
|
if event is InputEventMouseMotion:
|
||||||
|
if (
|
||||||
|
_mouse_down
|
||||||
|
and event.global_position.x > 0
|
||||||
|
and event.global_position.y < DisplayServer.window_get_size().y
|
||||||
|
):
|
||||||
|
if vertical_resize:
|
||||||
|
resize_control.size.y += event.relative.y
|
||||||
|
resize_control.size.x += event.relative.x
|
||||||
|
elif event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
|
_mouse_down = event.pressed
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_left_input(event: InputEvent):
|
||||||
|
if event is InputEventMouseMotion:
|
||||||
|
if (
|
||||||
|
_mouse_down
|
||||||
|
and event.global_position.x > 0
|
||||||
|
and event.global_position.y < DisplayServer.window_get_size().y
|
||||||
|
):
|
||||||
|
var start_size = resize_control.size
|
||||||
|
resize_control.size.x -= event.relative.x
|
||||||
|
if resize_control.size.x != start_size.x:
|
||||||
|
resize_control.global_position.x += event.relative.x
|
||||||
|
|
||||||
|
if vertical_resize:
|
||||||
|
resize_control.size.y += event.relative.y
|
||||||
|
elif event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
|
_mouse_down = event.pressed
|
||||||
|
queue_redraw()
|
8
addons/gut/gui/ResizeHandle.tscn
Normal file
8
addons/gut/gui/ResizeHandle.tscn
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bvrqqgjpyouse"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/gut/gui/ResizeHandle.gd" id="1_oi5ed"]
|
||||||
|
|
||||||
|
[node name="ResizeHandle" type="ColorRect"]
|
||||||
|
custom_minimum_size = Vector2(20, 20)
|
||||||
|
color = Color(1, 1, 1, 0)
|
||||||
|
script = ExtResource("1_oi5ed")
|
360
addons/gut/gui/ResultsTree.gd
Normal file
360
addons/gut/gui/ResultsTree.gd
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
@tool
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
var _show_orphans = true
|
||||||
|
var show_orphans = true:
|
||||||
|
get:
|
||||||
|
return _show_orphans
|
||||||
|
set(val):
|
||||||
|
_show_orphans = val
|
||||||
|
|
||||||
|
var _hide_passing = true
|
||||||
|
var hide_passing = true:
|
||||||
|
get:
|
||||||
|
return _hide_passing
|
||||||
|
set(val):
|
||||||
|
_hide_passing = val
|
||||||
|
|
||||||
|
var _icons = {
|
||||||
|
red = load("res://addons/gut/images/red.png"),
|
||||||
|
green = load("res://addons/gut/images/green.png"),
|
||||||
|
yellow = load("res://addons/gut/images/yellow.png"),
|
||||||
|
}
|
||||||
|
const _col_1_bg_color = Color(0, 0, 0, .1)
|
||||||
|
var _max_icon_width = 10
|
||||||
|
var _root: TreeItem
|
||||||
|
|
||||||
|
@onready var _ctrls = {tree = $Tree, lbl_overlay = $Tree/TextOverlay}
|
||||||
|
|
||||||
|
signal item_selected(script_path, inner_class, test_name, line_number)
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------
|
||||||
|
# Private
|
||||||
|
# -------------------
|
||||||
|
func _ready():
|
||||||
|
_root = _ctrls.tree.create_item()
|
||||||
|
_root = _ctrls.tree.create_item()
|
||||||
|
_ctrls.tree.set_hide_root(true)
|
||||||
|
_ctrls.tree.columns = 2
|
||||||
|
_ctrls.tree.set_column_expand(0, true)
|
||||||
|
_ctrls.tree.set_column_expand(1, false)
|
||||||
|
_ctrls.tree.set_column_clip_content(0, true)
|
||||||
|
|
||||||
|
$Tree.item_selected.connect(_on_tree_item_selected)
|
||||||
|
|
||||||
|
if get_parent() == get_tree().root:
|
||||||
|
_test_running_setup()
|
||||||
|
|
||||||
|
|
||||||
|
func _test_running_setup():
|
||||||
|
load_json_file("user://.gut_editor.json")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_tree_item_selected():
|
||||||
|
var item = _ctrls.tree.get_selected()
|
||||||
|
var item_meta = item.get_metadata(0)
|
||||||
|
var item_type = null
|
||||||
|
|
||||||
|
# Only select the left side of the tree item, cause I like that better.
|
||||||
|
# you can still click the right, but only the left gets highlighted.
|
||||||
|
if item.is_selected(1):
|
||||||
|
item.deselect(1)
|
||||||
|
item.select(0)
|
||||||
|
|
||||||
|
if item_meta == null:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
item_type = item_meta.type
|
||||||
|
|
||||||
|
var script_path = ""
|
||||||
|
var line = -1
|
||||||
|
var test_name = ""
|
||||||
|
var inner_class = ""
|
||||||
|
|
||||||
|
if item_type == "test":
|
||||||
|
var s_item = item.get_parent()
|
||||||
|
script_path = s_item.get_metadata(0)["path"]
|
||||||
|
inner_class = s_item.get_metadata(0)["inner_class"]
|
||||||
|
line = -1
|
||||||
|
test_name = item.get_text(0)
|
||||||
|
elif item_type == "assert":
|
||||||
|
var s_item = item.get_parent().get_parent()
|
||||||
|
script_path = s_item.get_metadata(0)["path"]
|
||||||
|
inner_class = s_item.get_metadata(0)["inner_class"]
|
||||||
|
line = _get_line_number_from_assert_msg(item.get_text(0))
|
||||||
|
test_name = item.get_parent().get_text(0)
|
||||||
|
elif item_type == "script":
|
||||||
|
script_path = item.get_metadata(0)["path"]
|
||||||
|
if item.get_parent() != _root:
|
||||||
|
inner_class = item.get_text(0)
|
||||||
|
line = -1
|
||||||
|
test_name = ""
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
item_selected.emit(script_path, inner_class, test_name, line)
|
||||||
|
|
||||||
|
|
||||||
|
func _get_line_number_from_assert_msg(msg):
|
||||||
|
var line = -1
|
||||||
|
if msg.find("at line") > 0:
|
||||||
|
line = msg.split("at line")[-1].split(" ")[-1].to_int()
|
||||||
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
func _get_path_and_inner_class_name_from_test_path(path):
|
||||||
|
var to_return = {path = "", inner_class = ""}
|
||||||
|
|
||||||
|
to_return.path = path
|
||||||
|
if !path.ends_with(".gd"):
|
||||||
|
var loc = path.find(".gd")
|
||||||
|
to_return.inner_class = path.split(".")[-1]
|
||||||
|
to_return.path = path.substr(0, loc + 3)
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
func _find_script_item_with_path(path):
|
||||||
|
var items = _root.get_children()
|
||||||
|
var to_return = null
|
||||||
|
|
||||||
|
var idx = 0
|
||||||
|
while idx < items.size() and to_return == null:
|
||||||
|
var item = items[idx]
|
||||||
|
if item.get_metadata(0).path == path:
|
||||||
|
to_return = item
|
||||||
|
else:
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
func _add_script_tree_item(script_path, script_json):
|
||||||
|
var path_info = _get_path_and_inner_class_name_from_test_path(script_path)
|
||||||
|
var item_text = script_path
|
||||||
|
var parent = _root
|
||||||
|
|
||||||
|
if path_info.inner_class != "":
|
||||||
|
parent = _find_script_item_with_path(path_info.path)
|
||||||
|
item_text = path_info.inner_class
|
||||||
|
if parent == null:
|
||||||
|
parent = _add_script_tree_item(path_info.path, {})
|
||||||
|
|
||||||
|
parent.get_metadata(0).inner_tests += script_json["props"]["tests"]
|
||||||
|
parent.get_metadata(0).inner_passing += script_json["props"]["tests"]
|
||||||
|
parent.get_metadata(0).inner_passing -= script_json["props"]["failures"]
|
||||||
|
parent.get_metadata(0).inner_passing -= script_json["props"]["pending"]
|
||||||
|
|
||||||
|
var total_text = str("All ", parent.get_metadata(0).inner_tests, " passed")
|
||||||
|
if parent.get_metadata(0).inner_passing != parent.get_metadata(0).inner_tests:
|
||||||
|
total_text = str(
|
||||||
|
parent.get_metadata(0).inner_passing,
|
||||||
|
"/",
|
||||||
|
parent.get_metadata(0).inner_tests,
|
||||||
|
" passed."
|
||||||
|
)
|
||||||
|
parent.set_text(1, total_text)
|
||||||
|
|
||||||
|
var item = _ctrls.tree.create_item(parent)
|
||||||
|
item.set_text(0, item_text)
|
||||||
|
var meta = {
|
||||||
|
"type": "script",
|
||||||
|
"path": path_info.path,
|
||||||
|
"inner_class": path_info.inner_class,
|
||||||
|
"json": script_json,
|
||||||
|
"inner_passing": 0,
|
||||||
|
"inner_tests": 0
|
||||||
|
}
|
||||||
|
item.set_metadata(0, meta)
|
||||||
|
item.set_custom_bg_color(1, _col_1_bg_color)
|
||||||
|
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
func _add_assert_item(text, icon, parent_item):
|
||||||
|
# print(' * adding assert')
|
||||||
|
var assert_item = _ctrls.tree.create_item(parent_item)
|
||||||
|
assert_item.set_icon_max_width(0, _max_icon_width)
|
||||||
|
assert_item.set_text(0, text)
|
||||||
|
assert_item.set_metadata(0, {"type": "assert"})
|
||||||
|
assert_item.set_icon(0, icon)
|
||||||
|
assert_item.set_custom_bg_color(1, _col_1_bg_color)
|
||||||
|
|
||||||
|
return assert_item
|
||||||
|
|
||||||
|
|
||||||
|
func _add_test_tree_item(test_name, test_json, script_item):
|
||||||
|
# print(' * adding test ', test_name)
|
||||||
|
var no_orphans_to_show = !_show_orphans or (_show_orphans and test_json.orphans == 0)
|
||||||
|
if _hide_passing and test_json["status"] == "pass" and no_orphans_to_show:
|
||||||
|
return
|
||||||
|
|
||||||
|
var item = _ctrls.tree.create_item(script_item)
|
||||||
|
var status = test_json["status"]
|
||||||
|
var meta = {"type": "test", "json": test_json}
|
||||||
|
|
||||||
|
item.set_text(0, test_name)
|
||||||
|
item.set_text(1, status)
|
||||||
|
item.set_text_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT)
|
||||||
|
item.set_custom_bg_color(1, _col_1_bg_color)
|
||||||
|
|
||||||
|
item.set_metadata(0, meta)
|
||||||
|
item.set_icon_max_width(0, _max_icon_width)
|
||||||
|
|
||||||
|
var orphan_text = "orphans"
|
||||||
|
if test_json.orphans == 1:
|
||||||
|
orphan_text = "orphan"
|
||||||
|
orphan_text = str(test_json.orphans, " ", orphan_text)
|
||||||
|
|
||||||
|
if status == "pass" and no_orphans_to_show:
|
||||||
|
item.set_icon(0, _icons.green)
|
||||||
|
elif status == "pass" and !no_orphans_to_show:
|
||||||
|
item.set_icon(0, _icons.yellow)
|
||||||
|
item.set_text(1, orphan_text)
|
||||||
|
elif status == "fail":
|
||||||
|
item.set_icon(0, _icons.red)
|
||||||
|
else:
|
||||||
|
item.set_icon(0, _icons.yellow)
|
||||||
|
|
||||||
|
if !_hide_passing:
|
||||||
|
for passing in test_json.passing:
|
||||||
|
_add_assert_item("pass: " + passing, _icons.green, item)
|
||||||
|
|
||||||
|
for failure in test_json.failing:
|
||||||
|
_add_assert_item("fail: " + failure.replace("\n", ""), _icons.red, item)
|
||||||
|
|
||||||
|
for pending in test_json.pending:
|
||||||
|
_add_assert_item("pending: " + pending.replace("\n", ""), _icons.yellow, item)
|
||||||
|
|
||||||
|
if status != "pass" and !no_orphans_to_show:
|
||||||
|
_add_assert_item(orphan_text, _icons.yellow, item)
|
||||||
|
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
func _add_script_to_tree(key, script_json):
|
||||||
|
var tests = script_json["tests"]
|
||||||
|
var test_keys = tests.keys()
|
||||||
|
var s_item = _add_script_tree_item(key, script_json)
|
||||||
|
var bad_count = 0
|
||||||
|
|
||||||
|
for test_key in test_keys:
|
||||||
|
var t_item = _add_test_tree_item(test_key, tests[test_key], s_item)
|
||||||
|
if tests[test_key].status != "pass":
|
||||||
|
bad_count += 1
|
||||||
|
elif t_item != null:
|
||||||
|
t_item.collapsed = true
|
||||||
|
|
||||||
|
if s_item.get_children().size() == 0:
|
||||||
|
s_item.free()
|
||||||
|
else:
|
||||||
|
var total_text = str("All ", test_keys.size(), " passed")
|
||||||
|
if bad_count == 0:
|
||||||
|
s_item.collapsed = true
|
||||||
|
else:
|
||||||
|
total_text = str(test_keys.size() - bad_count, "/", test_keys.size(), " passed")
|
||||||
|
s_item.set_text(1, total_text)
|
||||||
|
|
||||||
|
|
||||||
|
func _free_childless_scripts():
|
||||||
|
var items = _root.get_children()
|
||||||
|
for item in items:
|
||||||
|
var next_item = item.get_next()
|
||||||
|
if item.get_children().size() == 0:
|
||||||
|
item.free()
|
||||||
|
item = next_item
|
||||||
|
|
||||||
|
|
||||||
|
func _show_all_passed():
|
||||||
|
if _root.get_children().size() == 0:
|
||||||
|
add_centered_text("Everything passed!")
|
||||||
|
|
||||||
|
|
||||||
|
func _load_result_tree(j):
|
||||||
|
var scripts = j["test_scripts"]["scripts"]
|
||||||
|
var script_keys = scripts.keys()
|
||||||
|
# if we made it here, the json is valid and we did something, otherwise the
|
||||||
|
# 'nothing to see here' should be visible.
|
||||||
|
clear_centered_text()
|
||||||
|
|
||||||
|
for key in script_keys:
|
||||||
|
if scripts[key]["props"]["tests"] > 0:
|
||||||
|
_add_script_to_tree(key, scripts[key])
|
||||||
|
|
||||||
|
_free_childless_scripts()
|
||||||
|
_show_all_passed()
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------
|
||||||
|
# Public
|
||||||
|
# -------------------
|
||||||
|
func load_json_file(path):
|
||||||
|
var file = FileAccess.open(path, FileAccess.READ)
|
||||||
|
var text = ""
|
||||||
|
if file != null:
|
||||||
|
text = file.get_as_text()
|
||||||
|
|
||||||
|
if text != "":
|
||||||
|
var test_json_conv = JSON.new()
|
||||||
|
var result = test_json_conv.parse(text)
|
||||||
|
if result != OK:
|
||||||
|
add_centered_text(
|
||||||
|
str(
|
||||||
|
path,
|
||||||
|
" has invalid json in it \n",
|
||||||
|
"Error ",
|
||||||
|
result,
|
||||||
|
"@",
|
||||||
|
test_json_conv.get_error_line(),
|
||||||
|
"\n",
|
||||||
|
test_json_conv.get_error_message()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
var data = test_json_conv.get_data()
|
||||||
|
load_json_results(data)
|
||||||
|
else:
|
||||||
|
add_centered_text(str(path, " was empty or does not exist."))
|
||||||
|
|
||||||
|
|
||||||
|
func load_json_results(j):
|
||||||
|
clear()
|
||||||
|
_load_result_tree(j)
|
||||||
|
|
||||||
|
|
||||||
|
func clear():
|
||||||
|
_ctrls.tree.clear()
|
||||||
|
_root = _ctrls.tree.create_item()
|
||||||
|
|
||||||
|
|
||||||
|
func set_summary_min_width(width):
|
||||||
|
_ctrls.tree.set_column_custom_minimum_width(1, width)
|
||||||
|
|
||||||
|
|
||||||
|
func add_centered_text(t):
|
||||||
|
_ctrls.lbl_overlay.visible = true
|
||||||
|
_ctrls.lbl_overlay.text = t
|
||||||
|
|
||||||
|
|
||||||
|
func clear_centered_text():
|
||||||
|
_ctrls.lbl_overlay.visible = false
|
||||||
|
_ctrls.lbl_overlay.text = ""
|
||||||
|
|
||||||
|
|
||||||
|
func collapse_all():
|
||||||
|
set_collapsed_on_all(_root, true)
|
||||||
|
|
||||||
|
|
||||||
|
func expand_all():
|
||||||
|
set_collapsed_on_all(_root, false)
|
||||||
|
|
||||||
|
|
||||||
|
func set_collapsed_on_all(item, value):
|
||||||
|
item.set_collapsed_recursive(value)
|
||||||
|
if item == _root and value:
|
||||||
|
item.set_collapsed(false)
|
||||||
|
|
||||||
|
|
||||||
|
func get_selected():
|
||||||
|
return _ctrls.tree.get_selected()
|
32
addons/gut/gui/ResultsTree.tscn
Normal file
32
addons/gut/gui/ResultsTree.tscn
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://dls5r5f6157nq"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://addons/gut/gui/ResultsTree.gd" id="1_b4uub"]
|
||||||
|
|
||||||
|
[node name="ResultsTree" type="VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(10, 10)
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = -70.0
|
||||||
|
offset_bottom = -104.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
script = ExtResource("1_b4uub")
|
||||||
|
|
||||||
|
[node name="Tree" type="Tree" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
columns = 2
|
||||||
|
hide_root = true
|
||||||
|
|
||||||
|
[node name="TextOverlay" type="Label" parent="Tree"]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
|
@ -16,7 +16,7 @@ var _editors = null
|
||||||
var _cur_editor = null
|
var _cur_editor = null
|
||||||
var _last_line = -1
|
var _last_line = -1
|
||||||
var _cur_script_path = null
|
var _cur_script_path = null
|
||||||
var _last_info = null
|
var _last_info = {script = null, inner_class = null, test_method = null}
|
||||||
|
|
||||||
signal run_tests(what)
|
signal run_tests(what)
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ func _ready():
|
||||||
_ctrls.btn_script.visible = false
|
_ctrls.btn_script.visible = false
|
||||||
_ctrls.btn_inner.visible = false
|
_ctrls.btn_inner.visible = false
|
||||||
_ctrls.btn_method.visible = false
|
_ctrls.btn_method.visible = false
|
||||||
|
_ctrls.arrow_1.visible = false
|
||||||
|
_ctrls.arrow_2.visible = false
|
||||||
|
|
||||||
|
|
||||||
# ----------------
|
# ----------------
|
||||||
|
@ -34,11 +36,13 @@ func _ready():
|
||||||
func _set_editor(which):
|
func _set_editor(which):
|
||||||
_last_line = -1
|
_last_line = -1
|
||||||
if _cur_editor != null and _cur_editor.get_ref():
|
if _cur_editor != null and _cur_editor.get_ref():
|
||||||
_cur_editor.get_ref().disconnect("cursor_changed", Callable(self, "_on_cursor_changed"))
|
# _cur_editor.get_ref().disconnect('cursor_changed',Callable(self,'_on_cursor_changed'))
|
||||||
|
_cur_editor.get_ref().caret_changed.disconnect(_on_cursor_changed)
|
||||||
|
|
||||||
if which != null:
|
if which != null:
|
||||||
_cur_editor = weakref(which)
|
_cur_editor = weakref(which)
|
||||||
which.connect("cursor_changed", Callable(self, "_on_cursor_changed"), [which])
|
which.caret_changed.connect(_on_cursor_changed.bind(which))
|
||||||
|
# which.connect('cursor_changed',Callable(self,'_on_cursor_changed'),[which])
|
||||||
|
|
||||||
_last_line = which.get_caret_line()
|
_last_line = which.get_caret_line()
|
||||||
_last_info = _editors.get_line_info()
|
_last_info = _editors.get_line_info()
|
||||||
|
@ -52,12 +56,12 @@ func _update_buttons(info):
|
||||||
_ctrls.btn_inner.visible = info.inner_class != null
|
_ctrls.btn_inner.visible = info.inner_class != null
|
||||||
_ctrls.arrow_1.visible = info.inner_class != null
|
_ctrls.arrow_1.visible = info.inner_class != null
|
||||||
_ctrls.btn_inner.text = str(info.inner_class)
|
_ctrls.btn_inner.text = str(info.inner_class)
|
||||||
_ctrls.btn_inner.hint_tooltip = str("Run all tests in Inner-Test-Class ", info.inner_class)
|
_ctrls.btn_inner.tooltip_text = str("Run all tests in Inner-Test-Class ", info.inner_class)
|
||||||
|
|
||||||
_ctrls.btn_method.visible = info.test_method != null
|
_ctrls.btn_method.visible = info.test_method != null
|
||||||
_ctrls.arrow_2.visible = info.test_method != null
|
_ctrls.arrow_2.visible = info.test_method != null
|
||||||
_ctrls.btn_method.text = str(info.test_method)
|
_ctrls.btn_method.text = str(info.test_method)
|
||||||
_ctrls.btn_method.hint_tooltip = str("Run test ", info.test_method)
|
_ctrls.btn_method.tooltip_text = str("Run test ", info.test_method)
|
||||||
|
|
||||||
# The button's new size won't take effect until the next frame.
|
# The button's new size won't take effect until the next frame.
|
||||||
# This appears to be what was causing the button to not be clickable the
|
# This appears to be what was causing the button to not be clickable the
|
||||||
|
@ -66,7 +70,7 @@ func _update_buttons(info):
|
||||||
|
|
||||||
|
|
||||||
func _update_size():
|
func _update_size():
|
||||||
custom_minimum_size.x = _ctrls.btn_method.size.x + _ctrls.btn_method.rect_position.x
|
custom_minimum_size.x = _ctrls.btn_method.size.x + _ctrls.btn_method.position.x
|
||||||
|
|
||||||
|
|
||||||
# ----------------
|
# ----------------
|
||||||
|
@ -110,9 +114,12 @@ func set_script_text_editors(value):
|
||||||
func activate_for_script(path):
|
func activate_for_script(path):
|
||||||
_ctrls.btn_script.visible = true
|
_ctrls.btn_script.visible = true
|
||||||
_ctrls.btn_script.text = path.get_file()
|
_ctrls.btn_script.text = path.get_file()
|
||||||
_ctrls.btn_script.hint_tooltip = str("Run all tests in script ", path)
|
_ctrls.btn_script.tooltip_text = str("Run all tests in script ", path)
|
||||||
_cur_script_path = path
|
_cur_script_path = path
|
||||||
_editors.refresh()
|
_editors.refresh()
|
||||||
|
# We have to wait a beat for the visibility to change on
|
||||||
|
# the editors, otherwise we always get the first one.
|
||||||
|
await get_tree().process_frame
|
||||||
_set_editor(_editors.get_current_text_edit())
|
_set_editor(_editors.get_current_text_edit())
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,7 +152,4 @@ func search_current_editor_for_text(txt):
|
||||||
var result = te.search(txt, 0, 0, 0)
|
var result = te.search(txt, 0, 0, 0)
|
||||||
var to_return = -1
|
var to_return = -1
|
||||||
|
|
||||||
if result.size() > 0:
|
|
||||||
to_return = result[TextEdit.SEARCH_RESULT_LINE]
|
|
||||||
|
|
||||||
return to_return
|
return to_return
|
||||||
|
|
|
@ -1,79 +1,64 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=4 format=3 uid="uid://0yunjxtaa8iw"]
|
||||||
|
|
||||||
[ext_resource path="res://addons/gut/gui/RunAtCursor.gd" type="Script" id=1]
|
[ext_resource type="Script" path="res://addons/gut/gui/RunAtCursor.gd" id="1"]
|
||||||
[ext_resource path="res://addons/gut/gui/play.png" type="Texture2D" id=2]
|
[ext_resource type="Texture2D" uid="uid://cr6tvdv0ve6cv" path="res://addons/gut/gui/play.png" id="2"]
|
||||||
[ext_resource path="res://addons/gut/gui/arrow.png" type="Texture2D" id=3]
|
[ext_resource type="Texture2D" uid="uid://6wra5rxmfsrl" path="res://addons/gut/gui/arrow.png" id="3"]
|
||||||
|
|
||||||
[node name="RunAtCursor" type="Control"]
|
[node name="RunAtCursor" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
offset_right = 1.0
|
offset_right = 1.0
|
||||||
offset_bottom = -527.0
|
offset_bottom = -527.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
script = ExtResource( 1 )
|
script = ExtResource("1")
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="HBox" type="HBoxContainer" parent="."]
|
[node name="HBox" type="HBoxContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="LblNoneSelected" type="Label" parent="HBox"]
|
[node name="LblNoneSelected" type="Label" parent="HBox"]
|
||||||
offset_top = 29.0
|
layout_mode = 2
|
||||||
offset_right = 50.0
|
|
||||||
offset_bottom = 43.0
|
|
||||||
text = "<None>"
|
text = "<None>"
|
||||||
|
|
||||||
[node name="BtnRunScript" type="Button" parent="HBox"]
|
[node name="BtnRunScript" type="Button" parent="HBox"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 54.0
|
layout_mode = 2
|
||||||
offset_right = 140.0
|
|
||||||
offset_bottom = 73.0
|
|
||||||
text = "<script>"
|
text = "<script>"
|
||||||
icon = ExtResource( 2 )
|
icon = ExtResource("2")
|
||||||
|
|
||||||
[node name="Arrow1" type="TextureButton" parent="HBox"]
|
[node name="Arrow1" type="TextureButton" parent="HBox"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 54.0
|
|
||||||
offset_right = 78.0
|
|
||||||
offset_bottom = 73.0
|
|
||||||
custom_minimum_size = Vector2(24, 0)
|
custom_minimum_size = Vector2(24, 0)
|
||||||
texture_normal = ExtResource( 3 )
|
layout_mode = 2
|
||||||
expand = true
|
texture_normal = ExtResource("3")
|
||||||
stretch_mode = 3
|
stretch_mode = 3
|
||||||
|
|
||||||
[node name="BtnRunInnerClass" type="Button" parent="HBox"]
|
[node name="BtnRunInnerClass" type="Button" parent="HBox"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 134.0
|
layout_mode = 2
|
||||||
offset_right = 243.0
|
|
||||||
offset_bottom = 73.0
|
|
||||||
text = "<inner class>"
|
text = "<inner class>"
|
||||||
icon = ExtResource( 2 )
|
icon = ExtResource("2")
|
||||||
|
|
||||||
[node name="Arrow2" type="TextureButton" parent="HBox"]
|
[node name="Arrow2" type="TextureButton" parent="HBox"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 54.0
|
|
||||||
offset_right = 78.0
|
|
||||||
offset_bottom = 73.0
|
|
||||||
custom_minimum_size = Vector2(24, 0)
|
custom_minimum_size = Vector2(24, 0)
|
||||||
texture_normal = ExtResource( 3 )
|
layout_mode = 2
|
||||||
expand = true
|
texture_normal = ExtResource("3")
|
||||||
stretch_mode = 3
|
stretch_mode = 3
|
||||||
|
|
||||||
[node name="BtnRunMethod" type="Button" parent="HBox"]
|
[node name="BtnRunMethod" type="Button" parent="HBox"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 247.0
|
layout_mode = 2
|
||||||
offset_right = 337.0
|
|
||||||
offset_bottom = 73.0
|
|
||||||
text = "<method>"
|
text = "<method>"
|
||||||
icon = ExtResource( 2 )
|
icon = ExtResource("2")
|
||||||
|
|
||||||
[connection signal="pressed" from="HBox/BtnRunScript" to="." method="_on_BtnRunScript_pressed"]
|
[connection signal="pressed" from="HBox/BtnRunScript" to="." method="_on_BtnRunScript_pressed"]
|
||||||
[connection signal="pressed" from="HBox/BtnRunInnerClass" to="." method="_on_BtnRunInnerClass_pressed"]
|
[connection signal="pressed" from="HBox/BtnRunInnerClass" to="." method="_on_BtnRunInnerClass_pressed"]
|
||||||
|
|
|
@ -1,31 +1,14 @@
|
||||||
extends Control
|
|
||||||
@tool
|
@tool
|
||||||
|
extends Control
|
||||||
|
|
||||||
var _interface = null
|
var _interface = null
|
||||||
var _utils = load("res://addons/gut/utils.gd").new()
|
|
||||||
var _hide_passing = true
|
|
||||||
var _font = null
|
var _font = null
|
||||||
var _font_size = null
|
var _font_size = null
|
||||||
var _root = null
|
|
||||||
var _max_icon_width = 10
|
|
||||||
var _editors = null # script_text_editor_controls.gd
|
var _editors = null # script_text_editor_controls.gd
|
||||||
var _show_orphans = true
|
|
||||||
var _output_control = null
|
var _output_control = null
|
||||||
|
|
||||||
const _col_1_bg_color = Color(0, 0, 0, .1)
|
|
||||||
|
|
||||||
var _icons = {
|
|
||||||
red = load("res://addons/gut/images/red.png"),
|
|
||||||
green = load("res://addons/gut/images/green.png"),
|
|
||||||
yellow = load("res://addons/gut/images/yellow.png"),
|
|
||||||
}
|
|
||||||
|
|
||||||
signal search_for_text(text)
|
|
||||||
|
|
||||||
@onready var _ctrls = {
|
@onready var _ctrls = {
|
||||||
tree = $VBox/Output/Scroll/Tree,
|
tree = $VBox/Output/Scroll/Tree,
|
||||||
lbl_overlay = $VBox/Output/OverlayMessage,
|
|
||||||
chk_hide_passing = $VBox/Toolbar/HidePassing,
|
|
||||||
toolbar =
|
toolbar =
|
||||||
{
|
{
|
||||||
toolbar = $VBox/Toolbar,
|
toolbar = $VBox/Toolbar,
|
||||||
|
@ -40,9 +23,36 @@ signal search_for_text(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
var f = null
|
||||||
|
if $FontSampler.get_label_settings() == null:
|
||||||
|
f = get_theme_default_font()
|
||||||
|
else:
|
||||||
|
f = $FontSampler.get_label_settings().font
|
||||||
|
var s_size = f.get_string_size("000 of 000 passed")
|
||||||
|
_ctrls.tree.set_summary_min_width(s_size.x)
|
||||||
|
|
||||||
|
_set_toolbutton_icon(_ctrls.toolbar.collapse, "CollapseTree", "c")
|
||||||
|
_set_toolbutton_icon(_ctrls.toolbar.collapse_all, "CollapseTree", "c")
|
||||||
|
_set_toolbutton_icon(_ctrls.toolbar.expand, "ExpandTree", "e")
|
||||||
|
_set_toolbutton_icon(_ctrls.toolbar.expand_all, "ExpandTree", "e")
|
||||||
|
_set_toolbutton_icon(_ctrls.toolbar.show_script, "Script", "ss")
|
||||||
|
_set_toolbutton_icon(_ctrls.toolbar.scroll_output, "Font", "so")
|
||||||
|
|
||||||
|
_ctrls.tree.hide_passing = true
|
||||||
|
_ctrls.toolbar.hide_passing.button_pressed = false
|
||||||
|
_ctrls.tree.show_orphans = true
|
||||||
|
_ctrls.tree.item_selected.connect(_on_item_selected)
|
||||||
|
|
||||||
|
if get_parent() == get_tree().root:
|
||||||
|
_test_running_setup()
|
||||||
|
|
||||||
|
call_deferred("_update_min_width")
|
||||||
|
|
||||||
|
|
||||||
func _test_running_setup():
|
func _test_running_setup():
|
||||||
_hide_passing = true
|
_ctrls.tree.hide_passing = true
|
||||||
_show_orphans = true
|
_ctrls.tree.show_orphans = true
|
||||||
var _gut_config = load("res://addons/gut/gut_config.gd").new()
|
var _gut_config = load("res://addons/gut/gut_config.gd").new()
|
||||||
_gut_config.load_panel_options("res://.gut_editor_config.json")
|
_gut_config.load_panel_options("res://.gut_editor_config.json")
|
||||||
set_font(
|
set_font(
|
||||||
|
@ -50,7 +60,7 @@ func _test_running_setup():
|
||||||
)
|
)
|
||||||
|
|
||||||
_ctrls.toolbar.hide_passing.text = "[hp]"
|
_ctrls.toolbar.hide_passing.text = "[hp]"
|
||||||
load_json_file("user://.gut_editor.json")
|
_ctrls.tree.load_json_file("user://.gut_editor.json")
|
||||||
|
|
||||||
|
|
||||||
func _set_toolbutton_icon(btn, icon_name, text):
|
func _set_toolbutton_icon(btn, icon_name, text):
|
||||||
|
@ -60,51 +70,17 @@ func _set_toolbutton_icon(btn, icon_name, text):
|
||||||
btn.text = str("[", text, "]")
|
btn.text = str("[", text, "]")
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
var f = null
|
|
||||||
if $FontSampler.get_label_settings() == null:
|
|
||||||
f = get_theme_default_font()
|
|
||||||
else:
|
|
||||||
f = $FontSampler.get_label_settings().font
|
|
||||||
var s_size = f.get_string_size("000 of 000 passed")
|
|
||||||
_root = _ctrls.tree.create_item()
|
|
||||||
_ctrls.tree.set_hide_root(true)
|
|
||||||
_ctrls.tree.columns = 2
|
|
||||||
_ctrls.tree.set_column_expand(0, true)
|
|
||||||
_ctrls.tree.set_column_expand(1, false)
|
|
||||||
_ctrls.tree.set_column_custom_minimum_width(1, s_size.x)
|
|
||||||
|
|
||||||
_set_toolbutton_icon(_ctrls.toolbar.collapse, "CollapseTree", "c")
|
|
||||||
_set_toolbutton_icon(_ctrls.toolbar.collapse_all, "CollapseTree", "c")
|
|
||||||
_set_toolbutton_icon(_ctrls.toolbar.expand, "ExpandTree", "e")
|
|
||||||
_set_toolbutton_icon(_ctrls.toolbar.expand_all, "ExpandTree", "e")
|
|
||||||
_set_toolbutton_icon(_ctrls.toolbar.show_script, "Script", "ss")
|
|
||||||
_set_toolbutton_icon(_ctrls.toolbar.scroll_output, "Font", "so")
|
|
||||||
|
|
||||||
_ctrls.toolbar.hide_passing.set(
|
|
||||||
"custom_icons/checked", get_theme_icon("GuiVisibilityHidden", "EditorIcons")
|
|
||||||
)
|
|
||||||
_ctrls.toolbar.hide_passing.set(
|
|
||||||
"custom_icons/unchecked", get_theme_icon("GuiVisibilityVisible", "EditorIcons")
|
|
||||||
)
|
|
||||||
|
|
||||||
if get_parent() == get_tree().root:
|
|
||||||
_test_running_setup()
|
|
||||||
|
|
||||||
call_deferred("_update_min_width")
|
|
||||||
|
|
||||||
|
|
||||||
func _update_min_width():
|
func _update_min_width():
|
||||||
custom_minimum_size.x = _ctrls.toolbar.toolbar.size.x
|
custom_minimum_size.x = _ctrls.toolbar.toolbar.size.x
|
||||||
|
|
||||||
|
|
||||||
func _open_file(path, line_number):
|
func _open_script_in_editor(path, line_number):
|
||||||
if _interface == null:
|
if _interface == null:
|
||||||
print("Too soon, wait a bit and try again.")
|
print("Too soon, wait a bit and try again.")
|
||||||
return
|
return
|
||||||
|
|
||||||
var r = load(path)
|
var r = load(path)
|
||||||
if line_number != -1:
|
if line_number != null and line_number != -1:
|
||||||
_interface.edit_script(r, line_number)
|
_interface.edit_script(r, line_number)
|
||||||
else:
|
else:
|
||||||
_interface.edit_script(r)
|
_interface.edit_script(r)
|
||||||
|
@ -113,211 +89,6 @@ func _open_file(path, line_number):
|
||||||
_interface.set_main_screen_editor("Script")
|
_interface.set_main_screen_editor("Script")
|
||||||
|
|
||||||
|
|
||||||
func _add_script_tree_item(script_path, script_json):
|
|
||||||
var path_info = _get_path_and_inner_class_name_from_test_path(script_path)
|
|
||||||
# print('* adding script ', path_info)
|
|
||||||
var item_text = script_path
|
|
||||||
var parent = _root
|
|
||||||
|
|
||||||
if path_info.inner_class != "":
|
|
||||||
parent = _find_script_item_with_path(path_info.path)
|
|
||||||
item_text = path_info.inner_class
|
|
||||||
if parent == null:
|
|
||||||
parent = _add_script_tree_item(path_info.path, {})
|
|
||||||
|
|
||||||
var item = _ctrls.tree.create_item(parent)
|
|
||||||
item.set_text(0, item_text)
|
|
||||||
var meta = {
|
|
||||||
"type": "script",
|
|
||||||
"path": path_info.path,
|
|
||||||
"inner_class": path_info.inner_class,
|
|
||||||
"json": script_json
|
|
||||||
}
|
|
||||||
item.set_metadata(0, meta)
|
|
||||||
item.set_custom_bg_color(1, _col_1_bg_color)
|
|
||||||
|
|
||||||
return item
|
|
||||||
|
|
||||||
|
|
||||||
func _add_assert_item(text, icon, parent_item):
|
|
||||||
# print(' * adding assert')
|
|
||||||
var assert_item = _ctrls.tree.create_item(parent_item)
|
|
||||||
assert_item.set_icon_max_width(0, _max_icon_width)
|
|
||||||
assert_item.set_text(0, text)
|
|
||||||
assert_item.set_metadata(0, {"type": "assert"})
|
|
||||||
assert_item.set_icon(0, icon)
|
|
||||||
assert_item.set_custom_bg_color(1, _col_1_bg_color)
|
|
||||||
|
|
||||||
return assert_item
|
|
||||||
|
|
||||||
|
|
||||||
func _add_test_tree_item(test_name, test_json, script_item):
|
|
||||||
# print(' * adding test ', test_name)
|
|
||||||
var no_orphans_to_show = !_show_orphans or (_show_orphans and test_json.orphans == 0)
|
|
||||||
if _hide_passing and test_json["status"] == "pass" and no_orphans_to_show:
|
|
||||||
return
|
|
||||||
|
|
||||||
var item = _ctrls.tree.create_item(script_item)
|
|
||||||
var status = test_json["status"]
|
|
||||||
var meta = {"type": "test", "json": test_json}
|
|
||||||
|
|
||||||
item.set_text(0, test_name)
|
|
||||||
item.set_text(1, status)
|
|
||||||
item.set_text_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT)
|
|
||||||
item.set_custom_bg_color(1, _col_1_bg_color)
|
|
||||||
|
|
||||||
item.set_metadata(0, meta)
|
|
||||||
item.set_icon_max_width(0, _max_icon_width)
|
|
||||||
|
|
||||||
var orphan_text = "orphans"
|
|
||||||
if test_json.orphans == 1:
|
|
||||||
orphan_text = "orphan"
|
|
||||||
orphan_text = str(test_json.orphans, " ", orphan_text)
|
|
||||||
|
|
||||||
if status == "pass" and no_orphans_to_show:
|
|
||||||
item.set_icon(0, _icons.green)
|
|
||||||
elif status == "pass" and !no_orphans_to_show:
|
|
||||||
item.set_icon(0, _icons.yellow)
|
|
||||||
item.set_text(1, orphan_text)
|
|
||||||
elif status == "fail":
|
|
||||||
item.set_icon(0, _icons.red)
|
|
||||||
else:
|
|
||||||
item.set_icon(0, _icons.yellow)
|
|
||||||
|
|
||||||
if !_hide_passing:
|
|
||||||
for passing in test_json.passing:
|
|
||||||
_add_assert_item("pass: " + passing, _icons.green, item)
|
|
||||||
|
|
||||||
for failure in test_json.failing:
|
|
||||||
_add_assert_item("fail: " + failure.replace("\n", ""), _icons.red, item)
|
|
||||||
|
|
||||||
for pending in test_json.pending:
|
|
||||||
_add_assert_item("pending: " + pending.replace("\n", ""), _icons.yellow, item)
|
|
||||||
|
|
||||||
if status != "pass" and !no_orphans_to_show:
|
|
||||||
_add_assert_item(orphan_text, _icons.yellow, item)
|
|
||||||
|
|
||||||
return item
|
|
||||||
|
|
||||||
|
|
||||||
func _load_result_tree(j):
|
|
||||||
var scripts = j["test_scripts"]["scripts"]
|
|
||||||
var script_keys = scripts.keys()
|
|
||||||
# if we made it here, the json is valid and we did something, otherwise the
|
|
||||||
# 'nothing to see here' should be visible.
|
|
||||||
clear_centered_text()
|
|
||||||
|
|
||||||
var _last_script_item = null
|
|
||||||
for key in script_keys:
|
|
||||||
var tests = scripts[key]["tests"]
|
|
||||||
var test_keys = tests.keys()
|
|
||||||
var s_item = _add_script_tree_item(key, scripts[key])
|
|
||||||
var bad_count = 0
|
|
||||||
|
|
||||||
for test_key in test_keys:
|
|
||||||
var t_item = _add_test_tree_item(test_key, tests[test_key], s_item)
|
|
||||||
if tests[test_key].status != "pass":
|
|
||||||
bad_count += 1
|
|
||||||
elif t_item != null:
|
|
||||||
t_item.collapsed = true
|
|
||||||
|
|
||||||
# get_children returns the first child or null. its a dumb name.
|
|
||||||
if s_item.get_children() == null:
|
|
||||||
# var m = s_item.get_metadata(0)
|
|
||||||
# print('!! Deleting ', m.path, ' ', m.inner_class)
|
|
||||||
s_item.free()
|
|
||||||
else:
|
|
||||||
var total_text = str(test_keys.size(), " passed")
|
|
||||||
# s_item.set_text_alignment(1, s_item.ALIGN_LEFT)
|
|
||||||
if bad_count == 0:
|
|
||||||
s_item.collapsed = true
|
|
||||||
else:
|
|
||||||
total_text = str(test_keys.size() - bad_count, " of ", test_keys.size(), " passed")
|
|
||||||
s_item.set_text(1, total_text)
|
|
||||||
|
|
||||||
_free_childless_scripts()
|
|
||||||
_show_all_passed()
|
|
||||||
|
|
||||||
|
|
||||||
func _free_childless_scripts():
|
|
||||||
var items = _root.get_children()
|
|
||||||
for item in items:
|
|
||||||
var next_item = item.get_next()
|
|
||||||
if item.get_children() == null:
|
|
||||||
item.free()
|
|
||||||
item = next_item
|
|
||||||
|
|
||||||
|
|
||||||
func _find_script_item_with_path(path):
|
|
||||||
var items = _root.get_children()
|
|
||||||
var to_return = null
|
|
||||||
|
|
||||||
var idx = 0
|
|
||||||
while idx < items.size() and to_return == null:
|
|
||||||
var item = items[idx]
|
|
||||||
if item.get_metadata(0).path == path:
|
|
||||||
to_return = item
|
|
||||||
else:
|
|
||||||
idx += 1
|
|
||||||
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
|
|
||||||
func _get_line_number_from_assert_msg(msg):
|
|
||||||
var line = -1
|
|
||||||
if msg.find("at line") > 0:
|
|
||||||
line = msg.split("at line")[-1].split(" ")[-1].to_int()
|
|
||||||
return line
|
|
||||||
|
|
||||||
|
|
||||||
func _get_path_and_inner_class_name_from_test_path(path):
|
|
||||||
var to_return = {path = "", inner_class = ""}
|
|
||||||
|
|
||||||
to_return.path = path
|
|
||||||
if !path.ends_with(".gd"):
|
|
||||||
var loc = path.find(".gd")
|
|
||||||
to_return.inner_class = path.split(".")[-1]
|
|
||||||
to_return.path = path.substr(0, loc + 3)
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
|
|
||||||
func _handle_tree_item_select(item, force_scroll):
|
|
||||||
var item_type = item.get_metadata(0).type
|
|
||||||
|
|
||||||
var path = ""
|
|
||||||
var line = -1
|
|
||||||
var method_name = ""
|
|
||||||
var inner_class = ""
|
|
||||||
|
|
||||||
if item_type == "test":
|
|
||||||
var s_item = item.get_parent()
|
|
||||||
path = s_item.get_metadata(0)["path"]
|
|
||||||
inner_class = s_item.get_metadata(0)["inner_class"]
|
|
||||||
line = -1
|
|
||||||
method_name = item.get_text(0)
|
|
||||||
elif item_type == "assert":
|
|
||||||
var s_item = item.get_parent().get_parent()
|
|
||||||
path = s_item.get_metadata(0)["path"]
|
|
||||||
inner_class = s_item.get_metadata(0)["inner_class"]
|
|
||||||
|
|
||||||
line = _get_line_number_from_assert_msg(item.get_text(0))
|
|
||||||
method_name = item.get_parent().get_text(0)
|
|
||||||
elif item_type == "script":
|
|
||||||
path = item.get_metadata(0)["path"]
|
|
||||||
if item.get_parent() != _root:
|
|
||||||
inner_class = item.get_text(0)
|
|
||||||
line = -1
|
|
||||||
method_name = ""
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
var path_info = _get_path_and_inner_class_name_from_test_path(path)
|
|
||||||
if force_scroll or _ctrls.toolbar.show_script.pressed:
|
|
||||||
_goto_code(path, line, method_name, inner_class)
|
|
||||||
if force_scroll or _ctrls.toolbar.scroll_output.pressed:
|
|
||||||
_goto_output(path, method_name, inner_class)
|
|
||||||
|
|
||||||
|
|
||||||
# starts at beginning of text edit and searches for each search term, moving
|
# starts at beginning of text edit and searches for each search term, moving
|
||||||
# through the text as it goes; ensuring that, when done, it found the first
|
# through the text as it goes; ensuring that, when done, it found the first
|
||||||
# occurance of the last srting that happend after the first occurance of
|
# occurance of the last srting that happend after the first occurance of
|
||||||
|
@ -325,9 +96,12 @@ func _handle_tree_item_select(item, force_scroll):
|
||||||
# inner class that may have be a duplicate of a method name in a different
|
# inner class that may have be a duplicate of a method name in a different
|
||||||
# inner class)
|
# inner class)
|
||||||
func _get_line_number_for_seq_search(search_strings, te):
|
func _get_line_number_for_seq_search(search_strings, te):
|
||||||
# var te = _editors.get_current_text_edit()
|
if te == null:
|
||||||
|
print("No Text editor to get line number for")
|
||||||
|
return 0
|
||||||
|
|
||||||
var result = null
|
var result = null
|
||||||
var line = Vector2i(-1, -1)
|
var line = Vector2i(0, 0)
|
||||||
var s_flags = 0
|
var s_flags = 0
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
|
@ -348,7 +122,7 @@ func _goto_code(path, line, method_name = "", inner_class = ""):
|
||||||
print("going to ", [path, line, method_name, inner_class])
|
print("going to ", [path, line, method_name, inner_class])
|
||||||
return
|
return
|
||||||
|
|
||||||
_open_file(path, line)
|
_open_script_in_editor(path, line)
|
||||||
if line == -1:
|
if line == -1:
|
||||||
var search_strings = []
|
var search_strings = []
|
||||||
if inner_class != "":
|
if inner_class != "":
|
||||||
|
@ -357,8 +131,9 @@ func _goto_code(path, line, method_name = "", inner_class = ""):
|
||||||
if method_name != "":
|
if method_name != "":
|
||||||
search_strings.append(method_name)
|
search_strings.append(method_name)
|
||||||
|
|
||||||
|
await get_tree().process_frame
|
||||||
line = _get_line_number_for_seq_search(search_strings, _editors.get_current_text_edit())
|
line = _get_line_number_for_seq_search(search_strings, _editors.get_current_text_edit())
|
||||||
if line != -1:
|
if line != null and line != -1:
|
||||||
_interface.get_script_editor().goto_line(line)
|
_interface.get_script_editor().goto_line(line)
|
||||||
|
|
||||||
|
|
||||||
|
@ -375,44 +150,13 @@ func _goto_output(path, method_name, inner_class):
|
||||||
search_strings.append(method_name)
|
search_strings.append(method_name)
|
||||||
|
|
||||||
var line = _get_line_number_for_seq_search(search_strings, _output_control.get_rich_text_edit())
|
var line = _get_line_number_for_seq_search(search_strings, _output_control.get_rich_text_edit())
|
||||||
if line != -1:
|
if line != null and line != -1:
|
||||||
_output_control.scroll_to_line(line)
|
_output_control.scroll_to_line(line)
|
||||||
|
|
||||||
|
|
||||||
func _show_all_passed():
|
|
||||||
if _root.get_children() == null:
|
|
||||||
add_centered_text("Everything passed!")
|
|
||||||
|
|
||||||
|
|
||||||
func _set_collapsed_on_all(item, value):
|
|
||||||
if item == _root:
|
|
||||||
var node = _root.get_children()
|
|
||||||
while node != null:
|
|
||||||
node.call_recursive("set_collapsed", value)
|
|
||||||
node = node.get_next()
|
|
||||||
else:
|
|
||||||
item.call_recursive("set_collapsed", value)
|
|
||||||
|
|
||||||
|
|
||||||
# --------------
|
# --------------
|
||||||
# Events
|
# Events
|
||||||
# --------------
|
# --------------
|
||||||
func _on_Tree_item_selected():
|
|
||||||
# do not force scroll
|
|
||||||
var item = _ctrls.tree.get_selected()
|
|
||||||
_handle_tree_item_select(item, false)
|
|
||||||
# it just looks better if the left is always selected.
|
|
||||||
if item.is_selected(1):
|
|
||||||
item.deselect(1)
|
|
||||||
item.select(0)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_Tree_item_activated():
|
|
||||||
# force scroll
|
|
||||||
print("double clicked")
|
|
||||||
_handle_tree_item_select(_ctrls.tree.get_selected(), true)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_Collapse_pressed():
|
func _on_Collapse_pressed():
|
||||||
collapse_selected()
|
collapse_selected()
|
||||||
|
|
||||||
|
@ -430,55 +174,30 @@ func _on_ExpandAll_pressed():
|
||||||
|
|
||||||
|
|
||||||
func _on_Hide_Passing_pressed():
|
func _on_Hide_Passing_pressed():
|
||||||
_hide_passing = _ctrls.toolbar.hide_passing.button_pressed
|
_ctrls.tree.hide_passing = !_ctrls.toolbar.hide_passing.button_pressed
|
||||||
|
_ctrls.tree.load_json_file("user://.gut_editor.json")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_item_selected(script_path, inner_class, test_name, line):
|
||||||
|
if _ctrls.toolbar.show_script.button_pressed:
|
||||||
|
_goto_code(script_path, line, test_name, inner_class)
|
||||||
|
if _ctrls.toolbar.scroll_output.button_pressed:
|
||||||
|
_goto_output(script_path, test_name, inner_class)
|
||||||
|
|
||||||
|
|
||||||
# --------------
|
# --------------
|
||||||
# Public
|
# Public
|
||||||
# --------------
|
# --------------
|
||||||
func load_json_file(path):
|
|
||||||
var text = _utils.get_file_as_text(path)
|
|
||||||
if text != "":
|
|
||||||
var test_json_conv = JSON.new()
|
|
||||||
test_json_conv.parse(text)
|
|
||||||
var result = test_json_conv.get_data()
|
|
||||||
if result.error != OK:
|
|
||||||
add_centered_text(
|
|
||||||
str(
|
|
||||||
path,
|
|
||||||
" has invalid json in it \n",
|
|
||||||
"Error ",
|
|
||||||
result.error,
|
|
||||||
"@",
|
|
||||||
result.error_line,
|
|
||||||
"\n",
|
|
||||||
result.error_string
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
load_json_results(result.result)
|
|
||||||
else:
|
|
||||||
add_centered_text(str(path, " was empty or does not exist."))
|
|
||||||
|
|
||||||
|
|
||||||
func load_json_results(j):
|
|
||||||
clear()
|
|
||||||
add_centered_text("Nothing Here")
|
|
||||||
_load_result_tree(j)
|
|
||||||
|
|
||||||
|
|
||||||
func add_centered_text(t):
|
func add_centered_text(t):
|
||||||
_ctrls.lbl_overlay.text = t
|
_ctrls.tree.add_centered_text(t)
|
||||||
|
|
||||||
|
|
||||||
func clear_centered_text():
|
func clear_centered_text():
|
||||||
_ctrls.lbl_overlay.text = ""
|
_ctrls.tree.clear_centered_text()
|
||||||
|
|
||||||
|
|
||||||
func clear():
|
func clear():
|
||||||
_ctrls.tree.clear()
|
_ctrls.tree.clear()
|
||||||
_root = _ctrls.tree.create_item()
|
|
||||||
clear_centered_text()
|
clear_centered_text()
|
||||||
|
|
||||||
|
|
||||||
|
@ -491,27 +210,27 @@ func set_script_text_editors(value):
|
||||||
|
|
||||||
|
|
||||||
func collapse_all():
|
func collapse_all():
|
||||||
_set_collapsed_on_all(_root, true)
|
_ctrls.tree.collapse_all()
|
||||||
|
|
||||||
|
|
||||||
func expand_all():
|
func expand_all():
|
||||||
_set_collapsed_on_all(_root, false)
|
_ctrls.tree.expand_all()
|
||||||
|
|
||||||
|
|
||||||
func collapse_selected():
|
func collapse_selected():
|
||||||
var item = _ctrls.tree.get_selected()
|
var item = _ctrls.tree.get_selected()
|
||||||
if item != null:
|
if item != null:
|
||||||
_set_collapsed_on_all(item, true)
|
_ctrls.tree.set_collapsed_on_all(item, true)
|
||||||
|
|
||||||
|
|
||||||
func expand_selected():
|
func expand_selected():
|
||||||
var item = _ctrls.tree.get_selected()
|
var item = _ctrls.tree.get_selected()
|
||||||
if item != null:
|
if item != null:
|
||||||
_set_collapsed_on_all(item, false)
|
_ctrls.tree.set_collapsed_on_all(item, false)
|
||||||
|
|
||||||
|
|
||||||
func set_show_orphans(should):
|
func set_show_orphans(should):
|
||||||
_show_orphans = should
|
_ctrls.tree.show_orphans = should
|
||||||
|
|
||||||
|
|
||||||
func set_font(font_name, size):
|
func set_font(font_name, size):
|
||||||
|
@ -531,3 +250,7 @@ func set_font(font_name, size):
|
||||||
|
|
||||||
func set_output_control(value):
|
func set_output_control(value):
|
||||||
_output_control = value
|
_output_control = value
|
||||||
|
|
||||||
|
|
||||||
|
func load_json_results(j):
|
||||||
|
_ctrls.tree.load_json_results(j)
|
||||||
|
|
|
@ -1,157 +1,110 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=5 format=3 uid="uid://4gyyn12um08h"]
|
||||||
|
|
||||||
[ext_resource path="res://addons/gut/gui/RunResults.gd" type="Script" id=1]
|
[ext_resource type="Script" path="res://addons/gut/gui/RunResults.gd" id="1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dls5r5f6157nq" path="res://addons/gut/gui/ResultsTree.tscn" id="2_o808v"]
|
||||||
|
|
||||||
[sub_resource type="Image" id=3]
|
[sub_resource type="Image" id="Image_abbh7"]
|
||||||
data = {
|
data = {
|
||||||
"data": PackedByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
|
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||||
"format": "LumAlpha8",
|
"format": "RGBA8",
|
||||||
"height": 16,
|
"height": 16,
|
||||||
"mipmaps": false,
|
"mipmaps": false,
|
||||||
"width": 16
|
"width": 16
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id=2]
|
[sub_resource type="ImageTexture" id="ImageTexture_x655i"]
|
||||||
flags = 4
|
image = SubResource("Image_abbh7")
|
||||||
flags = 4
|
|
||||||
image = SubResource( 3 )
|
|
||||||
size = Vector2( 16, 16 )
|
|
||||||
|
|
||||||
[node name="RunResults" type="Control"]
|
[node name="RunResults" type="Control"]
|
||||||
offset_right = 595.0
|
custom_minimum_size = Vector2(345, 0)
|
||||||
offset_bottom = 459.0
|
layout_mode = 3
|
||||||
custom_minimum_size = Vector2( 302, 0 )
|
anchors_preset = 0
|
||||||
script = ExtResource( 1 )
|
offset_right = 709.0
|
||||||
|
offset_bottom = 321.0
|
||||||
|
script = ExtResource("1")
|
||||||
|
|
||||||
[node name="VBox" type="VBoxContainer" parent="."]
|
[node name="VBox" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
|
|
||||||
[node name="Toolbar" type="HBoxContainer" parent="VBox"]
|
[node name="Toolbar" type="HBoxContainer" parent="VBox"]
|
||||||
offset_right = 296.0
|
layout_mode = 2
|
||||||
offset_bottom = 24.0
|
|
||||||
size_flags_horizontal = 0
|
size_flags_horizontal = 0
|
||||||
|
|
||||||
[node name="Expand" type="Button" parent="VBox/Toolbar"]
|
[node name="Expand" type="Button" parent="VBox/Toolbar"]
|
||||||
offset_right = 28.0
|
layout_mode = 2
|
||||||
offset_bottom = 24.0
|
icon = SubResource("ImageTexture_x655i")
|
||||||
hint_tooltip = "Expand selected item and all children."
|
|
||||||
icon = SubResource( 2 )
|
|
||||||
|
|
||||||
[node name="Collapse" type="Button" parent="VBox/Toolbar"]
|
[node name="Collapse" type="Button" parent="VBox/Toolbar"]
|
||||||
offset_left = 32.0
|
layout_mode = 2
|
||||||
offset_right = 60.0
|
icon = SubResource("ImageTexture_x655i")
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Collapse selected item and all children."
|
|
||||||
icon = SubResource( 2 )
|
|
||||||
|
|
||||||
[node name="Sep" type="ColorRect" parent="VBox/Toolbar"]
|
[node name="Sep" type="ColorRect" parent="VBox/Toolbar"]
|
||||||
offset_left = 64.0
|
|
||||||
offset_right = 66.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
custom_minimum_size = Vector2(2, 0)
|
custom_minimum_size = Vector2(2, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="LblAll" type="Label" parent="VBox/Toolbar"]
|
[node name="LblAll" type="Label" parent="VBox/Toolbar"]
|
||||||
offset_left = 70.0
|
layout_mode = 2
|
||||||
offset_top = 5.0
|
|
||||||
offset_right = 91.0
|
|
||||||
offset_bottom = 19.0
|
|
||||||
text = "All:"
|
text = "All:"
|
||||||
align = 1
|
|
||||||
|
|
||||||
[node name="ExpandAll" type="Button" parent="VBox/Toolbar"]
|
[node name="ExpandAll" type="Button" parent="VBox/Toolbar"]
|
||||||
offset_left = 95.0
|
layout_mode = 2
|
||||||
offset_right = 123.0
|
icon = SubResource("ImageTexture_x655i")
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Expand All."
|
|
||||||
icon = SubResource( 2 )
|
|
||||||
|
|
||||||
[node name="CollapseAll" type="Button" parent="VBox/Toolbar"]
|
[node name="CollapseAll" type="Button" parent="VBox/Toolbar"]
|
||||||
offset_left = 127.0
|
layout_mode = 2
|
||||||
offset_right = 155.0
|
icon = SubResource("ImageTexture_x655i")
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Collapse all."
|
|
||||||
icon = SubResource( 2 )
|
|
||||||
|
|
||||||
[node name="Sep2" type="ColorRect" parent="VBox/Toolbar"]
|
[node name="Sep2" type="ColorRect" parent="VBox/Toolbar"]
|
||||||
offset_left = 159.0
|
|
||||||
offset_right = 161.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
custom_minimum_size = Vector2(2, 0)
|
custom_minimum_size = Vector2(2, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="HidePassing" type="CheckBox" parent="VBox/Toolbar"]
|
[node name="HidePassing" type="CheckBox" parent="VBox/Toolbar"]
|
||||||
offset_left = 165.0
|
layout_mode = 2
|
||||||
offset_right = 189.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Show/Hide passing tests. Takes effect on next run."
|
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
custom_icons/checked = SubResource( 2 )
|
text = "Passing"
|
||||||
custom_icons/unchecked = SubResource( 2 )
|
|
||||||
pressed = true
|
|
||||||
__meta__ = {
|
|
||||||
"_editor_description_": ""
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="Sep3" type="ColorRect" parent="VBox/Toolbar"]
|
[node name="Sep3" type="ColorRect" parent="VBox/Toolbar"]
|
||||||
offset_left = 193.0
|
|
||||||
offset_right = 195.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
custom_minimum_size = Vector2(2, 0)
|
custom_minimum_size = Vector2(2, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="LblSync" type="Label" parent="VBox/Toolbar"]
|
[node name="LblSync" type="Label" parent="VBox/Toolbar"]
|
||||||
offset_left = 199.0
|
layout_mode = 2
|
||||||
offset_top = 5.0
|
|
||||||
offset_right = 232.0
|
|
||||||
offset_bottom = 19.0
|
|
||||||
text = "Sync:"
|
text = "Sync:"
|
||||||
align = 1
|
|
||||||
|
|
||||||
[node name="ShowScript" type="Button" parent="VBox/Toolbar"]
|
[node name="ShowScript" type="Button" parent="VBox/Toolbar"]
|
||||||
offset_left = 236.0
|
layout_mode = 2
|
||||||
offset_right = 264.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Open script and scroll to line when a tree item is clicked."
|
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
pressed = true
|
button_pressed = true
|
||||||
icon = SubResource( 2 )
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="ScrollOutput" type="Button" parent="VBox/Toolbar"]
|
[node name="ScrollOutput" type="Button" parent="VBox/Toolbar"]
|
||||||
offset_left = 268.0
|
layout_mode = 2
|
||||||
offset_right = 296.0
|
|
||||||
offset_bottom = 24.0
|
|
||||||
hint_tooltip = "Scroll to related line in the output panel when tree item clicked."
|
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
pressed = true
|
button_pressed = true
|
||||||
icon = SubResource( 2 )
|
icon = SubResource("ImageTexture_x655i")
|
||||||
|
|
||||||
[node name="Output" type="Panel" parent="VBox"]
|
[node name="Output" type="Panel" parent="VBox"]
|
||||||
self_modulate = Color(1, 1, 1, 0.541176)
|
self_modulate = Color(1, 1, 1, 0.541176)
|
||||||
offset_top = 28.0
|
layout_mode = 2
|
||||||
offset_right = 595.0
|
|
||||||
offset_bottom = 459.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="Scroll" type="ScrollContainer" parent="VBox/Output"]
|
[node name="Scroll" type="ScrollContainer" parent="VBox/Output"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="Tree" type="Tree" parent="VBox/Output/Scroll"]
|
[node name="Tree" parent="VBox/Output/Scroll" instance=ExtResource("2_o808v")]
|
||||||
offset_right = 595.0
|
layout_mode = 2
|
||||||
offset_bottom = 431.0
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
columns = 2
|
|
||||||
hide_root = true
|
|
||||||
|
|
||||||
[node name="OverlayMessage" type="Label" parent="VBox/Output"]
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
align = 1
|
|
||||||
valign = 1
|
|
||||||
|
|
||||||
[node name="FontSampler" type="Label" parent="."]
|
[node name="FontSampler" type="Label" parent="."]
|
||||||
visible = false
|
visible = false
|
||||||
|
layout_mode = 0
|
||||||
offset_right = 40.0
|
offset_right = 40.0
|
||||||
offset_bottom = 14.0
|
offset_bottom = 14.0
|
||||||
text = "000 of 000 passed"
|
text = "000 of 000 passed"
|
||||||
|
@ -161,5 +114,3 @@ text = "000 of 000 passed"
|
||||||
[connection signal="pressed" from="VBox/Toolbar/ExpandAll" to="." method="_on_ExpandAll_pressed"]
|
[connection signal="pressed" from="VBox/Toolbar/ExpandAll" to="." method="_on_ExpandAll_pressed"]
|
||||||
[connection signal="pressed" from="VBox/Toolbar/CollapseAll" to="." method="_on_CollapseAll_pressed"]
|
[connection signal="pressed" from="VBox/Toolbar/CollapseAll" to="." method="_on_CollapseAll_pressed"]
|
||||||
[connection signal="pressed" from="VBox/Toolbar/HidePassing" to="." method="_on_Hide_Passing_pressed"]
|
[connection signal="pressed" from="VBox/Toolbar/HidePassing" to="." method="_on_Hide_Passing_pressed"]
|
||||||
[connection signal="item_activated" from="VBox/Output/Scroll/Tree" to="." method="_on_Tree_item_activated"]
|
|
||||||
[connection signal="item_selected" from="VBox/Output/Scroll/Tree" to="." method="_on_Tree_item_selected"]
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene format=2]
|
[gd_scene format=3 uid="uid://cvvvtsah38l0e"]
|
||||||
|
|
||||||
[node name="Settings" type="VBoxContainer"]
|
[node name="Settings" type="VBoxContainer"]
|
||||||
offset_right = 388.0
|
offset_right = 388.0
|
||||||
|
|
|
@ -36,24 +36,13 @@ func _display_shortcut():
|
||||||
func _is_shift_only_modifier():
|
func _is_shift_only_modifier():
|
||||||
return (
|
return (
|
||||||
_source_event.shift_pressed
|
_source_event.shift_pressed
|
||||||
and !(
|
and !(_source_event.alt_pressed or _source_event.ctrl_pressed or _source_event.meta_pressed)
|
||||||
_source_event.alt_pressed
|
|
||||||
or _source_event.command_pressed
|
|
||||||
or _source_event.ctrl_pressed
|
|
||||||
or _source_event.meta_pressed
|
|
||||||
)
|
|
||||||
and !_is_modifier(_source_event.keycode)
|
and !_is_modifier(_source_event.keycode)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _has_modifier(event):
|
func _has_modifier(event):
|
||||||
return (
|
return event.alt_pressed or event.ctrl_pressed or event.meta_pressed or event.shift_pressed
|
||||||
event.alt_pressed
|
|
||||||
or event.command_pressed
|
|
||||||
or event.ctrl_pressed
|
|
||||||
or event.meta_pressed
|
|
||||||
or event.shift_pressed
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _is_modifier(keycode):
|
func _is_modifier(keycode):
|
||||||
|
|
|
@ -1,77 +1,52 @@
|
||||||
[gd_scene load_steps=2 format=2]
|
[gd_scene load_steps=2 format=3 uid="uid://sfb1fw8j6ufu"]
|
||||||
|
|
||||||
[ext_resource path="res://addons/gut/gui/ShortcutButton.gd" type="Script" id=1]
|
[ext_resource type="Script" path="res://addons/gut/gui/ShortcutButton.gd" id="1"]
|
||||||
|
|
||||||
[node name="ShortcutButton" type="Control"]
|
[node name="ShortcutButton" type="Control"]
|
||||||
|
custom_minimum_size = Vector2(210, 30)
|
||||||
|
layout_mode = 3
|
||||||
anchor_right = 0.123
|
anchor_right = 0.123
|
||||||
anchor_bottom = 0.04
|
anchor_bottom = 0.04
|
||||||
offset_right = 33.048
|
offset_right = 68.304
|
||||||
offset_bottom = 1.0
|
offset_bottom = 6.08
|
||||||
custom_minimum_size = Vector2( 125, 25 )
|
script = ExtResource("1")
|
||||||
script = ExtResource( 1 )
|
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="Layout" type="HBoxContainer" parent="."]
|
[node name="Layout" type="HBoxContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="lblShortcut" type="Label" parent="Layout"]
|
[node name="lblShortcut" type="Label" parent="Layout"]
|
||||||
offset_right = 50.0
|
layout_mode = 2
|
||||||
offset_bottom = 25.0
|
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 7
|
size_flags_vertical = 7
|
||||||
text = "<None>"
|
text = "<None>"
|
||||||
align = 2
|
horizontal_alignment = 2
|
||||||
valign = 1
|
|
||||||
|
|
||||||
[node name="CenterContainer" type="CenterContainer" parent="Layout"]
|
[node name="CenterContainer" type="CenterContainer" parent="Layout"]
|
||||||
offset_left = 54.0
|
|
||||||
offset_right = 64.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(10, 0)
|
custom_minimum_size = Vector2(10, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="SetButton" type="Button" parent="Layout"]
|
[node name="SetButton" type="Button" parent="Layout"]
|
||||||
offset_left = 68.0
|
|
||||||
offset_right = 128.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(60, 0)
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
layout_mode = 2
|
||||||
text = "Set"
|
text = "Set"
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="SaveButton" type="Button" parent="Layout"]
|
[node name="SaveButton" type="Button" parent="Layout"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 82.0
|
|
||||||
offset_right = 142.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(60, 0)
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
layout_mode = 2
|
||||||
text = "Save"
|
text = "Save"
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="CancelButton" type="Button" parent="Layout"]
|
[node name="CancelButton" type="Button" parent="Layout"]
|
||||||
visible = false
|
visible = false
|
||||||
offset_left = 82.0
|
|
||||||
offset_right = 142.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(60, 0)
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
layout_mode = 2
|
||||||
text = "Cancel"
|
text = "Cancel"
|
||||||
__meta__ = {
|
|
||||||
"_edit_use_anchors_": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="ClearButton" type="Button" parent="Layout"]
|
[node name="ClearButton" type="Button" parent="Layout"]
|
||||||
offset_left = 132.0
|
|
||||||
offset_right = 192.0
|
|
||||||
offset_bottom = 25.0
|
|
||||||
custom_minimum_size = Vector2(60, 0)
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
layout_mode = 2
|
||||||
text = "Clear"
|
text = "Clear"
|
||||||
|
|
||||||
[connection signal="pressed" from="Layout/SetButton" to="." method="_on_SetButton_pressed"]
|
[connection signal="pressed" from="Layout/SetButton" to="." method="_on_SetButton_pressed"]
|
||||||
|
|
|
@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/arrow.png-2b5b2d838b5b3467cf300ac2da1630d9.ct
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
compress/mode=0
|
compress/mode=0
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
compress/lossy_quality=0.7
|
||||||
compress/hdr_compression=1
|
compress/hdr_compression=1
|
||||||
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
compress/channel_pack=0
|
compress/channel_pack=0
|
||||||
mipmaps/generate=false
|
mipmaps/generate=false
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
# Choose an existing directory from res://. Dialog allows for creating a
|
||||||
|
# directory.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class DirectoryCtrl:
|
class DirectoryCtrl:
|
||||||
extends HBoxContainer
|
extends HBoxContainer
|
||||||
|
@ -9,20 +11,20 @@ class DirectoryCtrl:
|
||||||
set(val):
|
set(val):
|
||||||
_txt_path.text = val
|
_txt_path.text = val
|
||||||
|
|
||||||
var _txt_path = LineEdit.new()
|
var _txt_path := LineEdit.new()
|
||||||
var _btn_dir = Button.new()
|
var _btn_dir := Button.new()
|
||||||
var _dialog = FileDialog.new()
|
var _dialog := FileDialog.new()
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
_btn_dir.text = "..."
|
_btn_dir.text = "..."
|
||||||
_btn_dir.connect("pressed", Callable(self, "_on_dir_button_pressed"))
|
_btn_dir.pressed.connect(_on_dir_button_pressed)
|
||||||
|
|
||||||
_txt_path.size_flags_horizontal = _txt_path.SIZE_EXPAND_FILL
|
_txt_path.size_flags_horizontal = _txt_path.SIZE_EXPAND_FILL
|
||||||
|
|
||||||
_dialog.mode = _dialog.FILE_MODE_OPEN_DIR
|
_dialog.file_mode = _dialog.FILE_MODE_OPEN_DIR
|
||||||
_dialog.unresizable = false
|
_dialog.unresizable = false
|
||||||
_dialog.connect("dir_selected", Callable(self, "_on_selected"))
|
_dialog.dir_selected.connect(_on_selected)
|
||||||
_dialog.connect("file_selected", Callable(self, "_on_selected"))
|
_dialog.file_selected.connect(_on_selected)
|
||||||
_dialog.size = Vector2(1000, 700)
|
_dialog.size = Vector2(1000, 700)
|
||||||
|
|
||||||
func _on_selected(path):
|
func _on_selected(path):
|
||||||
|
@ -42,12 +44,27 @@ class DirectoryCtrl:
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
# Choose an existing file in res://
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class FileCtrl:
|
class FileCtrl:
|
||||||
extends DirectoryCtrl
|
extends DirectoryCtrl
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
_dialog.mode = _dialog.FILE_MODE_OPEN_FILE
|
super._init()
|
||||||
|
_dialog.file_mode = _dialog.FILE_MODE_OPEN_FILE
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Choose a save location. Can pick anywhere on file system. Will warn if you
|
||||||
|
# pick a file that already exists.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class SaveFileAnywhere:
|
||||||
|
extends DirectoryCtrl
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
super._init()
|
||||||
|
_dialog.file_mode = _dialog.FILE_MODE_SAVE_FILE
|
||||||
|
_dialog.access = _dialog.ACCESS_FILESYSTEM
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -127,6 +144,11 @@ func _init(cont):
|
||||||
_base_control.add_child(lbl)
|
_base_control.add_child(lbl)
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(what):
|
||||||
|
if what == NOTIFICATION_PREDELETE:
|
||||||
|
_base_control.free()
|
||||||
|
|
||||||
|
|
||||||
# ------------------
|
# ------------------
|
||||||
# Private
|
# Private
|
||||||
# ------------------
|
# ------------------
|
||||||
|
@ -134,7 +156,7 @@ func _new_row(key, disp_text, value_ctrl, hint):
|
||||||
var ctrl = _base_control.duplicate()
|
var ctrl = _base_control.duplicate()
|
||||||
var lbl = ctrl.get_child(0)
|
var lbl = ctrl.get_child(0)
|
||||||
|
|
||||||
lbl.hint_tooltip = hint
|
lbl.tooltip_text = hint
|
||||||
lbl.text = disp_text
|
lbl.text = disp_text
|
||||||
_base_container.add_child(ctrl)
|
_base_container.add_child(ctrl)
|
||||||
|
|
||||||
|
@ -167,7 +189,7 @@ func _add_number(key, value, disp_text, v_min, v_max, hint = ""):
|
||||||
value_ctrl.max_value = v_max
|
value_ctrl.max_value = v_max
|
||||||
_wire_select_on_focus(value_ctrl.get_line_edit())
|
_wire_select_on_focus(value_ctrl.get_line_edit())
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
func _add_select(key, value, values, disp_text, hint = ""):
|
func _add_select(key, value, values, disp_text, hint = ""):
|
||||||
|
@ -180,7 +202,7 @@ func _add_select(key, value, values, disp_text, hint = ""):
|
||||||
value_ctrl.selected = select_idx
|
value_ctrl.selected = select_idx
|
||||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
func _add_value(key, value, disp_text, hint = ""):
|
func _add_value(key, value, disp_text, hint = ""):
|
||||||
|
@ -189,14 +211,14 @@ func _add_value(key, value, disp_text, hint = ""):
|
||||||
value_ctrl.text = value
|
value_ctrl.text = value
|
||||||
_wire_select_on_focus(value_ctrl)
|
_wire_select_on_focus(value_ctrl)
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
func _add_boolean(key, value, disp_text, hint = ""):
|
func _add_boolean(key, value, disp_text, hint = ""):
|
||||||
var value_ctrl = CheckBox.new()
|
var value_ctrl = CheckBox.new()
|
||||||
value_ctrl.button_pressed = value
|
value_ctrl.button_pressed = value
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
func _add_directory(key, value, disp_text, hint = ""):
|
func _add_directory(key, value, disp_text, hint = ""):
|
||||||
|
@ -205,7 +227,7 @@ func _add_directory(key, value, disp_text, hint = ""):
|
||||||
value_ctrl.text = value
|
value_ctrl.text = value
|
||||||
_wire_select_on_focus(value_ctrl.get_line_edit())
|
_wire_select_on_focus(value_ctrl.get_line_edit())
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
func _add_file(key, value, disp_text, hint = ""):
|
func _add_file(key, value, disp_text, hint = ""):
|
||||||
|
@ -214,7 +236,16 @@ func _add_file(key, value, disp_text, hint = ""):
|
||||||
value_ctrl.text = value
|
value_ctrl.text = value
|
||||||
_wire_select_on_focus(value_ctrl.get_line_edit())
|
_wire_select_on_focus(value_ctrl.get_line_edit())
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_save_file_anywhere(key, value, disp_text, hint = ""):
|
||||||
|
var value_ctrl = SaveFileAnywhere.new()
|
||||||
|
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||||
|
value_ctrl.text = value
|
||||||
|
_wire_select_on_focus(value_ctrl.get_line_edit())
|
||||||
|
|
||||||
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
func _add_color(key, value, disp_text, hint = ""):
|
func _add_color(key, value, disp_text, hint = ""):
|
||||||
|
@ -222,7 +253,7 @@ func _add_color(key, value, disp_text, hint = ""):
|
||||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||||
value_ctrl.color = value
|
value_ctrl.color = value
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
func _add_vector2(key, value, disp_text, hint = ""):
|
func _add_vector2(key, value, disp_text, hint = ""):
|
||||||
|
@ -232,7 +263,7 @@ func _add_vector2(key, value, disp_text, hint = ""):
|
||||||
_wire_select_on_focus(value_ctrl.x_spin.get_line_edit())
|
_wire_select_on_focus(value_ctrl.x_spin.get_line_edit())
|
||||||
_wire_select_on_focus(value_ctrl.y_spin.get_line_edit())
|
_wire_select_on_focus(value_ctrl.y_spin.get_line_edit())
|
||||||
|
|
||||||
_new_row(key, disp_text, value_ctrl, hint)
|
return _new_row(key, disp_text, value_ctrl, hint)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
|
@ -242,7 +273,6 @@ func _add_vector2(key, value, disp_text, hint = ""):
|
||||||
# Events
|
# Events
|
||||||
# ------------------
|
# ------------------
|
||||||
func _wire_select_on_focus(which):
|
func _wire_select_on_focus(which):
|
||||||
pass
|
|
||||||
which.connect("focus_entered", _on_ctrl_focus_highlight.bind(which))
|
which.connect("focus_entered", _on_ctrl_focus_highlight.bind(which))
|
||||||
which.connect("focus_exited", _on_ctrl_focus_unhighlight.bind(which))
|
which.connect("focus_exited", _on_ctrl_focus_unhighlight.bind(which))
|
||||||
|
|
||||||
|
@ -273,8 +303,7 @@ func get_config_issues():
|
||||||
var path = _cfg_ctrls[key].text
|
var path = _cfg_ctrls[key].text
|
||||||
if path != null and path != "":
|
if path != null and path != "":
|
||||||
has_directory = true
|
has_directory = true
|
||||||
var dir = DirAccess.open(".")
|
if !DirAccess.dir_exists_absolute(path):
|
||||||
if !dir.dir_exists(path):
|
|
||||||
to_return.append(str("Test directory ", path, " does not exist."))
|
to_return.append(str("Test directory ", path, " does not exist."))
|
||||||
|
|
||||||
if !has_directory:
|
if !has_directory:
|
||||||
|
@ -286,8 +315,23 @@ func get_config_issues():
|
||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# --------------
|
||||||
|
# SUPER dumb but VERY fun hack to hide settings. The various _add methods will
|
||||||
|
# return what they add. If you want to hide it, just assign the result to this.
|
||||||
|
# YES, I could have just put .visible at the end, but I didn't think of that
|
||||||
|
# until just now, and this was fun, non-permanent and the .visible at the end
|
||||||
|
# isn't as obvious as hide_this =
|
||||||
|
#
|
||||||
|
# Also, we can't just skip adding the controls because other things are looking
|
||||||
|
# for them and things start to blow up if you don't add them.
|
||||||
|
var hide_this = null:
|
||||||
|
set(val):
|
||||||
|
val.visible = false
|
||||||
|
# --------------
|
||||||
|
|
||||||
|
|
||||||
func set_options(options):
|
func set_options(options):
|
||||||
_add_title("Settings")
|
_add_title("Settings") # ----------------------------------
|
||||||
_add_number(
|
_add_number(
|
||||||
"log_level",
|
"log_level",
|
||||||
options.log_level,
|
options.log_level,
|
||||||
|
@ -321,26 +365,48 @@ func set_options(options):
|
||||||
"Exit on Success",
|
"Exit on Success",
|
||||||
"Exit if there are no failures. Does nothing if 'Exit on Finish' is enabled."
|
"Exit if there are no failures. Does nothing if 'Exit on Finish' is enabled."
|
||||||
)
|
)
|
||||||
|
var ds = _add_select(
|
||||||
|
"double_strategy",
|
||||||
|
"Script Only",
|
||||||
|
["Include Native", "Script Only"],
|
||||||
|
"Double Strategy",
|
||||||
|
(
|
||||||
|
'"Include Native" will include native methods in Doubles. "Script Only" will not. '
|
||||||
|
+ "\n"
|
||||||
|
+ "The native method override warning is disabled when creating Doubles."
|
||||||
|
+ "\n"
|
||||||
|
+ "This is the default, you can override this at the script level or when creating doubles."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_cfg_ctrls["double_strategy"].selected = GutUtils.get_enum_value(
|
||||||
|
options.double_strategy, GutUtils.DOUBLE_STRATEGY, GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY
|
||||||
|
)
|
||||||
|
_add_boolean(
|
||||||
|
"errors_cause_failure",
|
||||||
|
!options.errors_do_not_cause_failure,
|
||||||
|
"Errors cause failures.",
|
||||||
|
"When GUT generates an error (not an engine error) it causes tests to fail."
|
||||||
|
)
|
||||||
|
|
||||||
_add_title("Panel Output")
|
_add_title("Panel Output") # ----------------------------------
|
||||||
_add_select(
|
_add_select(
|
||||||
"output_font_name",
|
"output_font_name",
|
||||||
options.panel_options.font_name,
|
options.panel_options.output_font_name,
|
||||||
_avail_fonts,
|
_avail_fonts,
|
||||||
"Font",
|
"Font",
|
||||||
"The name of the font to use when running tests and in the output panel to the left."
|
"The name of the font to use when running tests and in the output panel to the left."
|
||||||
)
|
)
|
||||||
_add_number(
|
_add_number(
|
||||||
"output_font_size",
|
"output_font_size",
|
||||||
options.panel_options.font_size,
|
options.panel_options.output_font_size,
|
||||||
"Font Size",
|
"Font Size",
|
||||||
5,
|
5,
|
||||||
100,
|
100,
|
||||||
"The font size to use when running tests and in the output panel to the left."
|
"The font size to use when running tests and in the output panel to the left."
|
||||||
)
|
)
|
||||||
|
|
||||||
_add_title("Runner Window")
|
_add_title("Runner Window") # ----------------------------------
|
||||||
_add_boolean(
|
hide_this = _add_boolean(
|
||||||
"gut_on_top",
|
"gut_on_top",
|
||||||
options.gut_on_top,
|
options.gut_on_top,
|
||||||
"On Top",
|
"On Top",
|
||||||
|
@ -349,7 +415,7 @@ func set_options(options):
|
||||||
_add_number(
|
_add_number(
|
||||||
"opacity", options.opacity, "Opacity", 0, 100, "The opacity of GUT when tests are running."
|
"opacity", options.opacity, "Opacity", 0, 100, "The opacity of GUT when tests are running."
|
||||||
)
|
)
|
||||||
_add_boolean(
|
hide_this = _add_boolean(
|
||||||
"should_maximize",
|
"should_maximize",
|
||||||
options.should_maximize,
|
options.should_maximize,
|
||||||
"Maximize",
|
"Maximize",
|
||||||
|
@ -362,7 +428,7 @@ func set_options(options):
|
||||||
"The runner will be in compact mode. This overrides Maximize."
|
"The runner will be in compact mode. This overrides Maximize."
|
||||||
)
|
)
|
||||||
|
|
||||||
_add_title("Runner Appearance")
|
_add_title("Runner Appearance") # ----------------------------------
|
||||||
_add_select(
|
_add_select(
|
||||||
"font_name",
|
"font_name",
|
||||||
options.font_name,
|
options.font_name,
|
||||||
|
@ -378,7 +444,7 @@ func set_options(options):
|
||||||
100,
|
100,
|
||||||
"The font size for text output in the Gut Runner."
|
"The font size for text output in the Gut Runner."
|
||||||
)
|
)
|
||||||
_add_color(
|
hide_this = _add_color(
|
||||||
"font_color",
|
"font_color",
|
||||||
options.font_color,
|
options.font_color,
|
||||||
"Font Color",
|
"Font Color",
|
||||||
|
@ -397,7 +463,7 @@ func set_options(options):
|
||||||
"Disable formatting and colors used in the Runner. Does not affect panel output."
|
"Disable formatting and colors used in the Runner. Does not affect panel output."
|
||||||
)
|
)
|
||||||
|
|
||||||
_add_title("Test Directories")
|
_add_title("Test Directories") # ----------------------------------
|
||||||
_add_boolean(
|
_add_boolean(
|
||||||
"include_subdirs",
|
"include_subdirs",
|
||||||
options.include_subdirs,
|
options.include_subdirs,
|
||||||
|
@ -411,11 +477,11 @@ func set_options(options):
|
||||||
|
|
||||||
_add_directory(str("directory_", i), value, str("Directory ", i))
|
_add_directory(str("directory_", i), value, str("Directory ", i))
|
||||||
|
|
||||||
_add_title("XML Output")
|
_add_title("XML Output") # ----------------------------------
|
||||||
_add_value(
|
_add_save_file_anywhere(
|
||||||
"junit_xml_file",
|
"junit_xml_file",
|
||||||
options.junit_xml_file,
|
options.junit_xml_file,
|
||||||
"Output Path3D",
|
"Output Path",
|
||||||
(
|
(
|
||||||
"Path3D and filename where GUT should create a JUnit compliant XML file. "
|
"Path3D and filename where GUT should create a JUnit compliant XML file. "
|
||||||
+ "This file will contain the results of the last test run. To avoid "
|
+ "This file will contain the results of the last test run. To avoid "
|
||||||
|
@ -429,7 +495,7 @@ func set_options(options):
|
||||||
"Include a timestamp in the filename so that each run gets its own xml file."
|
"Include a timestamp in the filename so that each run gets its own xml file."
|
||||||
)
|
)
|
||||||
|
|
||||||
_add_title("Hooks")
|
_add_title("Hooks") # ----------------------------------
|
||||||
_add_file(
|
_add_file(
|
||||||
"pre_run_script",
|
"pre_run_script",
|
||||||
options.pre_run_script,
|
options.pre_run_script,
|
||||||
|
@ -443,7 +509,7 @@ func set_options(options):
|
||||||
"This script will be run by GUT after all tests are run."
|
"This script will be run by GUT after all tests are run."
|
||||||
)
|
)
|
||||||
|
|
||||||
_add_title("Misc")
|
_add_title("Misc") # ----------------------------------
|
||||||
_add_value(
|
_add_value(
|
||||||
"prefix", options.prefix, "Script Prefix", "The filename prefix for all test scripts."
|
"prefix", options.prefix, "Script Prefix", "The filename prefix for all test scripts."
|
||||||
)
|
)
|
||||||
|
@ -466,7 +532,7 @@ func set_options(options):
|
||||||
_cfg_ctrls.paint_after.step = .05
|
_cfg_ctrls.paint_after.step = .05
|
||||||
_cfg_ctrls.paint_after.value = options.paint_after
|
_cfg_ctrls.paint_after.value = options.paint_after
|
||||||
|
|
||||||
print("paint after = ", options.paint_after)
|
print("GUT config loaded")
|
||||||
|
|
||||||
|
|
||||||
func get_options(base_opts):
|
func get_options(base_opts):
|
||||||
|
@ -478,11 +544,13 @@ func get_options(base_opts):
|
||||||
to_return.hide_orphans = _cfg_ctrls.hide_orphans.button_pressed
|
to_return.hide_orphans = _cfg_ctrls.hide_orphans.button_pressed
|
||||||
to_return.should_exit = _cfg_ctrls.should_exit.button_pressed
|
to_return.should_exit = _cfg_ctrls.should_exit.button_pressed
|
||||||
to_return.should_exit_on_success = _cfg_ctrls.should_exit_on_success.button_pressed
|
to_return.should_exit_on_success = _cfg_ctrls.should_exit_on_success.button_pressed
|
||||||
|
to_return.double_strategy = _cfg_ctrls.double_strategy.selected
|
||||||
|
to_return.errors_do_not_cause_failure = !_cfg_ctrls.errors_cause_failure.button_pressed
|
||||||
|
|
||||||
#Output
|
#Output
|
||||||
to_return.panel_options.font_name = (_cfg_ctrls.output_font_name.get_item_text(
|
to_return.panel_options.font_name = _cfg_ctrls.output_font_name.get_item_text(
|
||||||
_cfg_ctrls.output_font_name.selected
|
_cfg_ctrls.output_font_name.selected
|
||||||
))
|
)
|
||||||
to_return.panel_options.font_size = _cfg_ctrls.output_font_size.value
|
to_return.panel_options.font_size = _cfg_ctrls.output_font_size.value
|
||||||
|
|
||||||
# Runner Appearance
|
# Runner Appearance
|
||||||
|
|
238
addons/gut/gui/gut_gui.gd
Normal file
238
addons/gut/gui/gut_gui.gd
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
extends Control
|
||||||
|
# ##############################################################################
|
||||||
|
# This is the decoupled GUI for gut.gd
|
||||||
|
#
|
||||||
|
# This is a "generic" interface between a GUI and gut.gd. It assumes there are
|
||||||
|
# certain controls with specific names. It will then interact with those
|
||||||
|
# controls based on signals emitted from gut.gd in order to give the user
|
||||||
|
# feedback about the progress of the test run and the results.
|
||||||
|
#
|
||||||
|
# Optional controls are marked as such in the _ctrls dictionary. The names
|
||||||
|
# of the controls can be found in _populate_ctrls.
|
||||||
|
# ##############################################################################
|
||||||
|
var _gut = null
|
||||||
|
|
||||||
|
var _ctrls = {
|
||||||
|
btn_continue = null,
|
||||||
|
path_dir = null,
|
||||||
|
path_file = null,
|
||||||
|
prog_script = null,
|
||||||
|
prog_test = null,
|
||||||
|
rtl = null, # optional
|
||||||
|
rtl_bg = null, # required if rtl exists
|
||||||
|
switch_modes = null,
|
||||||
|
time_label = null,
|
||||||
|
title = null,
|
||||||
|
title_bar = null,
|
||||||
|
}
|
||||||
|
|
||||||
|
var _title_mouse = {down = false}
|
||||||
|
|
||||||
|
var _resize_mouse = {down = false}
|
||||||
|
|
||||||
|
var _resize_left_mouse = {down = false}
|
||||||
|
|
||||||
|
signal switch_modes
|
||||||
|
|
||||||
|
var _max_position = Vector2(100, 100)
|
||||||
|
var _utils = null
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
_populate_ctrls()
|
||||||
|
|
||||||
|
_ctrls.btn_continue.visible = false
|
||||||
|
_ctrls.btn_continue.pressed.connect(_on_continue_pressed)
|
||||||
|
_ctrls.switch_modes.pressed.connect(_on_switch_modes_pressed)
|
||||||
|
_ctrls.title_bar.gui_input.connect(_on_title_bar_input)
|
||||||
|
|
||||||
|
_ctrls.prog_script.value = 0
|
||||||
|
_ctrls.prog_test.value = 0
|
||||||
|
_ctrls.path_dir.text = ""
|
||||||
|
_ctrls.path_file.text = ""
|
||||||
|
_ctrls.time_label.text = ""
|
||||||
|
|
||||||
|
_max_position = get_display_size() - Vector2(30, _ctrls.title_bar.size.y)
|
||||||
|
|
||||||
|
|
||||||
|
func _process(_delta):
|
||||||
|
if _gut != null and _gut.is_running():
|
||||||
|
set_elapsed_time(_gut.get_elapsed_time())
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------
|
||||||
|
# Private
|
||||||
|
# ------------------
|
||||||
|
func get_display_size():
|
||||||
|
return get_viewport().get_visible_rect().size
|
||||||
|
|
||||||
|
|
||||||
|
func _populate_ctrls():
|
||||||
|
# Brute force, but flexible. This allows for all the controls to exist
|
||||||
|
# anywhere, and as long as they all have the right name, they will be
|
||||||
|
# found.
|
||||||
|
_ctrls.btn_continue = _get_first_child_named("Continue", self)
|
||||||
|
_ctrls.path_dir = _get_first_child_named("Path", self)
|
||||||
|
_ctrls.path_file = _get_first_child_named("File", self)
|
||||||
|
_ctrls.prog_script = _get_first_child_named("ProgressScript", self)
|
||||||
|
_ctrls.prog_test = _get_first_child_named("ProgressTest", self)
|
||||||
|
_ctrls.rtl = _get_first_child_named("TestOutput", self)
|
||||||
|
_ctrls.rtl_bg = _get_first_child_named("OutputBG", self)
|
||||||
|
_ctrls.switch_modes = _get_first_child_named("SwitchModes", self)
|
||||||
|
_ctrls.time_label = _get_first_child_named("TimeLabel", self)
|
||||||
|
_ctrls.title = _get_first_child_named("Title", self)
|
||||||
|
_ctrls.title_bar = _get_first_child_named("TitleBar", self)
|
||||||
|
|
||||||
|
|
||||||
|
func _get_first_child_named(obj_name, parent_obj):
|
||||||
|
if parent_obj == null:
|
||||||
|
return null
|
||||||
|
|
||||||
|
var kids = parent_obj.get_children()
|
||||||
|
var index = 0
|
||||||
|
var to_return = null
|
||||||
|
|
||||||
|
while index < kids.size() and to_return == null:
|
||||||
|
if str(kids[index]).find(str(obj_name, ":")) != -1:
|
||||||
|
to_return = kids[index]
|
||||||
|
else:
|
||||||
|
to_return = _get_first_child_named(obj_name, kids[index])
|
||||||
|
if to_return == null:
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------
|
||||||
|
# Events
|
||||||
|
# ------------------
|
||||||
|
func _on_title_bar_input(event: InputEvent):
|
||||||
|
if event is InputEventMouseMotion:
|
||||||
|
if _title_mouse.down:
|
||||||
|
position += event.relative
|
||||||
|
position.x = clamp(position.x, 0, _max_position.x)
|
||||||
|
position.y = clamp(position.y, 0, _max_position.y)
|
||||||
|
elif event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
|
_title_mouse.down = event.pressed
|
||||||
|
|
||||||
|
|
||||||
|
func _on_continue_pressed():
|
||||||
|
_gut.end_teardown_pause()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_start_run():
|
||||||
|
if _ctrls.rtl != null:
|
||||||
|
_ctrls.rtl.clear()
|
||||||
|
set_num_scripts(_gut.get_test_collector().scripts.size())
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_end_run():
|
||||||
|
_ctrls.prog_test.value = _ctrls.prog_test.max_value
|
||||||
|
_ctrls.prog_script.value = _ctrls.prog_script.max_value
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_start_script(script_obj):
|
||||||
|
next_script(script_obj.get_full_name(), script_obj.tests.size())
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_end_script():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_start_test(test_name):
|
||||||
|
next_test(test_name)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_end_test():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_start_pause():
|
||||||
|
pause_before_teardown()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gut_end_pause():
|
||||||
|
_ctrls.btn_continue.visible = false
|
||||||
|
|
||||||
|
|
||||||
|
func _on_switch_modes_pressed():
|
||||||
|
switch_modes.emit()
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------
|
||||||
|
# Public
|
||||||
|
# ------------------
|
||||||
|
func set_num_scripts(val):
|
||||||
|
_ctrls.prog_script.value = 0
|
||||||
|
_ctrls.prog_script.max_value = val
|
||||||
|
|
||||||
|
|
||||||
|
func next_script(path, num_tests):
|
||||||
|
_ctrls.prog_script.value += 1
|
||||||
|
_ctrls.prog_test.value = 0
|
||||||
|
_ctrls.prog_test.max_value = num_tests
|
||||||
|
|
||||||
|
_ctrls.path_dir.text = path.get_base_dir()
|
||||||
|
_ctrls.path_file.text = path.get_file()
|
||||||
|
|
||||||
|
|
||||||
|
func next_test(test_name):
|
||||||
|
_ctrls.prog_test.value += 1
|
||||||
|
|
||||||
|
|
||||||
|
func pause_before_teardown():
|
||||||
|
_ctrls.btn_continue.visible = true
|
||||||
|
|
||||||
|
|
||||||
|
func set_gut(g):
|
||||||
|
if _gut == g:
|
||||||
|
return
|
||||||
|
_gut = g
|
||||||
|
g.start_run.connect(_on_gut_start_run)
|
||||||
|
g.end_run.connect(_on_gut_end_run)
|
||||||
|
|
||||||
|
g.start_script.connect(_on_gut_start_script)
|
||||||
|
g.end_script.connect(_on_gut_end_script)
|
||||||
|
|
||||||
|
g.start_test.connect(_on_gut_start_test)
|
||||||
|
g.end_test.connect(_on_gut_end_test)
|
||||||
|
|
||||||
|
g.start_pause_before_teardown.connect(_on_gut_start_pause)
|
||||||
|
g.end_pause_before_teardown.connect(_on_gut_end_pause)
|
||||||
|
|
||||||
|
|
||||||
|
func get_gut():
|
||||||
|
return _gut
|
||||||
|
|
||||||
|
|
||||||
|
func get_textbox():
|
||||||
|
return _ctrls.rtl
|
||||||
|
|
||||||
|
|
||||||
|
func set_elapsed_time(t):
|
||||||
|
_ctrls.time_label.text = str("%6.1f" % t, "s")
|
||||||
|
|
||||||
|
|
||||||
|
func set_bg_color(c):
|
||||||
|
_ctrls.rtl_bg.color = c
|
||||||
|
|
||||||
|
|
||||||
|
func set_title(text):
|
||||||
|
_ctrls.title.text = text
|
||||||
|
|
||||||
|
|
||||||
|
func to_top_left():
|
||||||
|
self.position = Vector2(5, 5)
|
||||||
|
|
||||||
|
|
||||||
|
func to_bottom_right():
|
||||||
|
var win_size = get_display_size()
|
||||||
|
self.position = win_size - Vector2(self.size) - Vector2(5, 5)
|
||||||
|
|
||||||
|
|
||||||
|
func align_right():
|
||||||
|
var win_size = get_display_size()
|
||||||
|
self.position.x = win_size.x - self.size.x - 5
|
||||||
|
self.position.y = 5
|
||||||
|
self.size.y = win_size.y - 10
|
|
@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/play.png-5c90e88e8136487a183a099d67a7de24.cte
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
compress/mode=0
|
compress/mode=0
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
compress/lossy_quality=0.7
|
||||||
compress/hdr_compression=1
|
compress/hdr_compression=1
|
||||||
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
compress/channel_pack=0
|
compress/channel_pack=0
|
||||||
mipmaps/generate=false
|
mipmaps/generate=false
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Holds weakrefs to a ScriptTextEditor and related children nodes
|
# Holds weakrefs to a ScriptTextEditor and related children nodes
|
||||||
# that might be useful. Though the TextEdit is really the only one, but
|
# that might be useful. Though the CodeEdit is really the only one, but
|
||||||
# since the tree may change, the first TextEdit under a CodeTextEditor is
|
# since the tree may change, the first TextEdit under a CodeTextEditor is
|
||||||
# the one we use...so we hold a ref to the CodeTextEditor too.
|
# the one we use...so we hold a ref to the CodeTextEditor too.
|
||||||
class ScriptEditorControlRef:
|
class ScriptEditorControlRef:
|
||||||
|
@ -10,6 +10,7 @@ class ScriptEditorControlRef:
|
||||||
func _init(script_edit):
|
func _init(script_edit):
|
||||||
_script_editor = weakref(script_edit)
|
_script_editor = weakref(script_edit)
|
||||||
_populate_controls()
|
_populate_controls()
|
||||||
|
# print("_script_editor = ", script_edit, ' vis = ', is_visible())
|
||||||
|
|
||||||
func _populate_controls():
|
func _populate_controls():
|
||||||
# who knows if the tree will change so get the first instance of each
|
# who knows if the tree will change so get the first instance of each
|
||||||
|
@ -27,7 +28,7 @@ class ScriptEditorControlRef:
|
||||||
var to_return = null
|
var to_return = null
|
||||||
|
|
||||||
while index < kids.size() and to_return == null:
|
while index < kids.size() and to_return == null:
|
||||||
if str(kids[index]).find(str("[", obj_name)) != -1:
|
if str(kids[index]).find(str("<", obj_name)) != -1:
|
||||||
to_return = kids[index]
|
to_return = kids[index]
|
||||||
else:
|
else:
|
||||||
to_return = _get_first_child_named(obj_name, kids[index])
|
to_return = _get_first_child_named(obj_name, kids[index])
|
||||||
|
@ -79,7 +80,7 @@ func _init(script_edit):
|
||||||
|
|
||||||
|
|
||||||
func _is_script_editor(obj):
|
func _is_script_editor(obj):
|
||||||
return str(obj).find("[ScriptTextEditor") != -1
|
return str(obj).find("<ScriptTextEditor") != -1
|
||||||
|
|
||||||
|
|
||||||
# Find the first ScriptTextEditor and then get its parent. Done this way
|
# Find the first ScriptTextEditor and then get its parent. Done this way
|
||||||
|
@ -106,14 +107,19 @@ func _populate_editors():
|
||||||
# easier than trying to find a place where it could be used by both.
|
# easier than trying to find a place where it could be used by both.
|
||||||
func _get_first_child_of_type_name(obj_name, parent_obj):
|
func _get_first_child_of_type_name(obj_name, parent_obj):
|
||||||
if parent_obj == null:
|
if parent_obj == null:
|
||||||
|
# print('aborting search for ', obj_name, ' parent is null')
|
||||||
return null
|
return null
|
||||||
|
|
||||||
var kids = parent_obj.get_children()
|
var kids = parent_obj.get_children()
|
||||||
var index = 0
|
var index = 0
|
||||||
var to_return = null
|
var to_return = null
|
||||||
|
|
||||||
|
var search_for = str("<", obj_name)
|
||||||
|
# print('searching for ', search_for, ' in ', parent_obj, ' kids ', kids.size())
|
||||||
while index < kids.size() and to_return == null:
|
while index < kids.size() and to_return == null:
|
||||||
if str(kids[index]).find(str("[", obj_name)) != -1:
|
var this_one = str(kids[index])
|
||||||
|
# print(search_for, ' :: ', this_one)
|
||||||
|
if this_one.find(search_for) != -1:
|
||||||
to_return = kids[index]
|
to_return = kids[index]
|
||||||
else:
|
else:
|
||||||
to_return = _get_first_child_of_type_name(obj_name, kids[index])
|
to_return = _get_first_child_of_type_name(obj_name, kids[index])
|
||||||
|
@ -140,10 +146,13 @@ func _get_class_name_from_line(text):
|
||||||
func refresh():
|
func refresh():
|
||||||
if _script_editors_parent == null:
|
if _script_editors_parent == null:
|
||||||
_find_script_editors_parent()
|
_find_script_editors_parent()
|
||||||
|
# print("script editors parent = ", _script_editors_parent)
|
||||||
|
|
||||||
if _script_editors_parent != null:
|
if _script_editors_parent != null:
|
||||||
_populate_editors()
|
_populate_editors()
|
||||||
|
|
||||||
|
# print("script editor controls = ", _script_editor_controls)
|
||||||
|
|
||||||
|
|
||||||
func get_current_text_edit():
|
func get_current_text_edit():
|
||||||
var cur_script_editor = null
|
var cur_script_editor = null
|
||||||
|
|
|
@ -1,36 +1,13 @@
|
||||||
# ##############################################################################
|
|
||||||
#(G)odot (U)nit (T)est class
|
|
||||||
#
|
|
||||||
# ##############################################################################
|
|
||||||
# The MIT License (MIT)
|
|
||||||
# =====================
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020 Tom "Butch" Wesley
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
#
|
|
||||||
# ##############################################################################
|
|
||||||
# View the readme at https://github.com/bitwes/Gut/blob/master/README.md for usage details.
|
|
||||||
# You should also check out the github wiki at: https://github.com/bitwes/Gut/wiki
|
|
||||||
# ##############################################################################
|
|
||||||
extends "res://addons/gut/gut_to_move.gd"
|
extends "res://addons/gut/gut_to_move.gd"
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
#
|
||||||
|
# View the readme at https://github.com/bitwes/Gut/blob/master/README.md for usage
|
||||||
|
# details. You should also check out the github wiki at:
|
||||||
|
# https://github.com/bitwes/Gut/wiki
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
|
|
||||||
# ###########################
|
# ###########################
|
||||||
# Constants
|
# Constants
|
||||||
# ###########################
|
# ###########################
|
||||||
|
@ -89,7 +66,7 @@ var temp_directory = _temp_directory:
|
||||||
|
|
||||||
var _log_level = 1
|
var _log_level = 1
|
||||||
## The log detail level. Valid values are 0 - 2. Larger values do not matter.
|
## The log detail level. Valid values are 0 - 2. Larger values do not matter.
|
||||||
var log_level = 1:
|
var log_level = _log_level:
|
||||||
get:
|
get:
|
||||||
return _log_level
|
return _log_level
|
||||||
set(val):
|
set(val):
|
||||||
|
@ -123,14 +100,17 @@ var include_subdirectories = _include_subdirectories:
|
||||||
set(val):
|
set(val):
|
||||||
_include_subdirectories = val
|
_include_subdirectories = val
|
||||||
|
|
||||||
var _double_strategy = 1
|
var _double_strategy = GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY
|
||||||
## TODO rework what this is and then document it here.
|
## TODO rework what this is and then document it here.
|
||||||
var double_strategy = 1:
|
var double_strategy = _double_strategy:
|
||||||
get:
|
get:
|
||||||
return _double_strategy
|
return _double_strategy
|
||||||
set(val):
|
set(val):
|
||||||
|
if GutUtils.DOUBLE_STRATEGY.values().has(val):
|
||||||
_double_strategy = val
|
_double_strategy = val
|
||||||
_doubler.set_strategy(double_strategy)
|
_doubler.set_strategy(double_strategy)
|
||||||
|
else:
|
||||||
|
_lgr.error(str("gut.gd: invalid double_strategy ", val))
|
||||||
|
|
||||||
var _pre_run_script = ""
|
var _pre_run_script = ""
|
||||||
## Path to the script that will be run before all tests are run. This script
|
## Path to the script that will be run before all tests are run. This script
|
||||||
|
@ -194,10 +174,6 @@ var unit_test_name = _unit_test_name:
|
||||||
set(val):
|
set(val):
|
||||||
_unit_test_name = val
|
_unit_test_name = val
|
||||||
|
|
||||||
# ###########################
|
|
||||||
# Public Properties
|
|
||||||
# ###########################
|
|
||||||
|
|
||||||
var _parameter_handler = null
|
var _parameter_handler = null
|
||||||
# This is populated by test.gd each time a paramterized test is encountered
|
# This is populated by test.gd each time a paramterized test is encountered
|
||||||
# for the first time.
|
# for the first time.
|
||||||
|
@ -230,6 +206,13 @@ var add_children_to = self:
|
||||||
set(val):
|
set(val):
|
||||||
_add_children_to = val
|
_add_children_to = val
|
||||||
|
|
||||||
|
var _treat_error_as_failure = true
|
||||||
|
var treat_error_as_failure = _treat_error_as_failure:
|
||||||
|
get:
|
||||||
|
return _treat_error_as_failure
|
||||||
|
set(val):
|
||||||
|
_treat_error_as_failure = val
|
||||||
|
|
||||||
# ------------
|
# ------------
|
||||||
# Read only
|
# Read only
|
||||||
# ------------
|
# ------------
|
||||||
|
@ -317,11 +300,11 @@ var _done = false
|
||||||
# msecs ticks when run was started
|
# msecs ticks when run was started
|
||||||
var _start_time = 0.0
|
var _start_time = 0.0
|
||||||
|
|
||||||
|
# Collected Test instance for the current test being run.
|
||||||
var _current_test = null
|
var _current_test = null
|
||||||
var _pause_before_teardown = false
|
var _pause_before_teardown = false
|
||||||
|
|
||||||
var _awaiter = _utils.Awaiter.new()
|
var _awaiter = _utils.Awaiter.new()
|
||||||
var _new_summary = null
|
|
||||||
|
|
||||||
# Used to cancel importing scripts if an error has occurred in the setup. This
|
# Used to cancel importing scripts if an error has occurred in the setup. This
|
||||||
# prevents tests from being run if they were exported and ensures that the
|
# prevents tests from being run if they were exported and ensures that the
|
||||||
|
@ -331,17 +314,14 @@ var _new_summary = null
|
||||||
# was not broken somewhere and remove if no longer used.
|
# was not broken somewhere and remove if no longer used.
|
||||||
var _cancel_import = false
|
var _cancel_import = false
|
||||||
|
|
||||||
# Used for proper assert tracking and printing during before_all
|
# this is how long Gut will wait when there are items that must be queued free
|
||||||
var _before_all_test_obj = load("res://addons/gut/test_collector.gd").Test.new()
|
# when a test completes (due to calls to add_child_autoqfree)
|
||||||
# Used for proper assert tracking and printing during after_all
|
var _auto_queue_free_delay = .1
|
||||||
var _after_all_test_obj = load("res://addons/gut/test_collector.gd").Test.new()
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _init():
|
func _init():
|
||||||
_before_all_test_obj.name = "before_all"
|
|
||||||
_after_all_test_obj.name = "after_all"
|
|
||||||
# When running tests for GUT itself, _utils has been setup to always return
|
# When running tests for GUT itself, _utils has been setup to always return
|
||||||
# a new logger so this does not set the gut instance on the base logger
|
# a new logger so this does not set the gut instance on the base logger
|
||||||
# when creating test instances of GUT.
|
# when creating test instances of GUT.
|
||||||
|
@ -390,6 +370,8 @@ func _notification(what):
|
||||||
test_script.free()
|
test_script.free()
|
||||||
|
|
||||||
_test_script_objects = []
|
_test_script_objects = []
|
||||||
|
if is_instance_valid(_awaiter):
|
||||||
|
_awaiter.free()
|
||||||
|
|
||||||
|
|
||||||
func _print_versions(send_all = true):
|
func _print_versions(send_all = true):
|
||||||
|
@ -450,8 +432,6 @@ func end_teardown_pause():
|
||||||
# Private
|
# Private
|
||||||
#
|
#
|
||||||
#####################
|
#####################
|
||||||
|
|
||||||
|
|
||||||
func _log_test_children_warning(test_script):
|
func _log_test_children_warning(test_script):
|
||||||
if !_lgr.is_type_enabled(_lgr.types.orphan):
|
if !_lgr.is_type_enabled(_lgr.types.orphan):
|
||||||
return
|
return
|
||||||
|
@ -474,48 +454,10 @@ func _log_test_children_warning(test_script):
|
||||||
_lgr.warn(msg)
|
_lgr.warn(msg)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
func _log_end_run():
|
||||||
# Convert the _summary dictionary into text
|
if _should_print_summary:
|
||||||
# ------------------------------------------------------------------------------
|
var summary = _utils.Summary.new(self)
|
||||||
func _print_summary():
|
summary.log_end_run()
|
||||||
if !_should_print_summary:
|
|
||||||
return
|
|
||||||
|
|
||||||
_lgr.log("\n\n\n")
|
|
||||||
_lgr.log("==============================================", _lgr.fmts.yellow)
|
|
||||||
_lgr.log("= Run Summary", _lgr.fmts.yellow)
|
|
||||||
_lgr.log("==============================================", _lgr.fmts.yellow)
|
|
||||||
|
|
||||||
_new_summary.log_summary_text(_lgr)
|
|
||||||
|
|
||||||
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"
|
|
||||||
_lgr.log(logger_text)
|
|
||||||
|
|
||||||
if _new_summary.get_totals().tests > 0:
|
|
||||||
var fmt = _lgr.fmts.green
|
|
||||||
var msg = (
|
|
||||||
str(_new_summary.get_totals().passing_tests)
|
|
||||||
+ " passed "
|
|
||||||
+ str(_new_summary.get_totals().failing_tests)
|
|
||||||
+ " failed. "
|
|
||||||
+ str("Tests finished in ", get_elapsed_time(), "s")
|
|
||||||
)
|
|
||||||
if _new_summary.get_totals().failing > 0:
|
|
||||||
fmt = _lgr.fmts.red
|
|
||||||
elif _new_summary.get_totals().pending > 0:
|
|
||||||
fmt = _lgr.fmts.yellow
|
|
||||||
|
|
||||||
_lgr.log(msg, fmt)
|
|
||||||
else:
|
|
||||||
_lgr.log("No tests ran", _lgr.fmts.red)
|
|
||||||
|
|
||||||
|
|
||||||
func _validate_hook_script(path):
|
func _validate_hook_script(path):
|
||||||
|
@ -527,7 +469,7 @@ func _validate_hook_script(path):
|
||||||
|
|
||||||
if FileAccess.file_exists(path):
|
if FileAccess.file_exists(path):
|
||||||
var inst = load(path).new()
|
var inst = load(path).new()
|
||||||
if inst and inst is _utils.HookScript:
|
if inst and inst is GutHookScript:
|
||||||
result.instance = inst
|
result.instance = inst
|
||||||
result.valid = true
|
result.valid = true
|
||||||
else:
|
else:
|
||||||
|
@ -558,7 +500,6 @@ func _init_run():
|
||||||
var valid = true
|
var valid = true
|
||||||
_test_collector.set_test_class_prefix(_inner_class_prefix)
|
_test_collector.set_test_class_prefix(_inner_class_prefix)
|
||||||
_test_script_objects = []
|
_test_script_objects = []
|
||||||
_new_summary = _utils.Summary.new()
|
|
||||||
_current_test = null
|
_current_test = null
|
||||||
_is_running = true
|
_is_running = true
|
||||||
|
|
||||||
|
@ -576,36 +517,13 @@ func _init_run():
|
||||||
# Print out run information and close out the run.
|
# Print out run information and close out the run.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _end_run():
|
func _end_run():
|
||||||
_print_summary()
|
_log_end_run()
|
||||||
p("\n")
|
|
||||||
|
|
||||||
# Do not count any of the _test_script_objects since these will be released
|
|
||||||
# when GUT is released.
|
|
||||||
_orphan_counter._counters.total += _test_script_objects.size()
|
|
||||||
if _orphan_counter.get_counter("total") > 0 and _lgr.is_type_enabled("orphan"):
|
|
||||||
_orphan_counter.print_orphans("total", _lgr)
|
|
||||||
p("Note: This count does not include GUT objects that will be freed upon exit.")
|
|
||||||
p(" It also does not include any orphans created by global scripts")
|
|
||||||
p(" loaded before tests were ran.")
|
|
||||||
p(str("Total orphans = ", _orphan_counter.orphan_count()))
|
|
||||||
|
|
||||||
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 + '"')
|
|
||||||
|
|
||||||
_is_running = false
|
_is_running = false
|
||||||
|
|
||||||
_run_hook_script(_post_run_script_instance)
|
_run_hook_script(_post_run_script_instance)
|
||||||
_export_results()
|
_export_results()
|
||||||
end_run.emit()
|
end_run.emit()
|
||||||
|
|
||||||
if _utils.should_display_latest_version:
|
|
||||||
p("")
|
|
||||||
p(str("GUT version ", _utils.latest_version, " is now available."))
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Add additional export types here.
|
# Add additional export types here.
|
||||||
|
@ -630,34 +548,12 @@ func _export_junit_xml():
|
||||||
p(str("Results saved to ", output_file))
|
p(str("Results saved to ", output_file))
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# 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 false
|
|
||||||
# TODO 4.0 Keep this until we know how they are going to handle the
|
|
||||||
# 4.0 equivalent of GDScriptFunctionState
|
|
||||||
# return script_result != null and \
|
|
||||||
# typeof(script_result) == TYPE_OBJECT and \
|
|
||||||
# script_result is GDScriptFunctionState and \
|
|
||||||
# script_result.is_valid()
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Print out the heading for a new script
|
# Print out the heading for a new script
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _print_script_heading(script):
|
func _print_script_heading(coll_script):
|
||||||
if _does_class_name_match(_inner_class_name, script.inner_class_name):
|
if _does_class_name_match(_inner_class_name, coll_script.inner_class_name):
|
||||||
var fmt = _lgr.fmts.underline
|
_lgr.log(str("\n\n", coll_script.get_full_name()), _lgr.fmts.underline)
|
||||||
var divider = "-----------------------------------------"
|
|
||||||
|
|
||||||
var text = ""
|
|
||||||
if script.inner_class_name == null:
|
|
||||||
text = script.path
|
|
||||||
else:
|
|
||||||
text = str(script.path, ".", script.inner_class_name)
|
|
||||||
_lgr.log("\n\n" + text, fmt)
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -713,15 +609,9 @@ func _get_indexes_matching_path(path):
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _run_parameterized_test(test_script, test_name):
|
func _run_parameterized_test(test_script, test_name):
|
||||||
await _run_test(test_script, test_name)
|
await _run_test(test_script, test_name)
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
|
||||||
# var script_result = await _run_test(test_script, test_name)
|
|
||||||
# if(_is_function_state(script_result)):
|
|
||||||
# # _run_tests does _wait_for_done so just wait on it to complete
|
|
||||||
# await script_result.COMPLETED
|
|
||||||
# ----
|
|
||||||
|
|
||||||
if _current_test.assert_count == 0 and !_current_test.pending:
|
if _current_test.assert_count == 0 and !_current_test.pending:
|
||||||
_lgr.warn("Test did not assert")
|
_lgr.risky("Test did not assert")
|
||||||
|
|
||||||
if _parameter_handler == null:
|
if _parameter_handler == null:
|
||||||
_lgr.error(
|
_lgr.error(
|
||||||
|
@ -742,14 +632,8 @@ func _run_parameterized_test(test_script, test_name):
|
||||||
while !_parameter_handler.is_done():
|
while !_parameter_handler.is_done():
|
||||||
var cur_assert_count = _current_test.assert_count
|
var cur_assert_count = _current_test.assert_count
|
||||||
await _run_test(test_script, test_name)
|
await _run_test(test_script, test_name)
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
|
||||||
# script_result = await _run_test(test_script, test_name)
|
|
||||||
# if(_is_function_state(script_result)):
|
|
||||||
# # _run_tests does _wait_for_done so just wait on it to complete
|
|
||||||
# await script_result.COMPLETED
|
|
||||||
# ----
|
|
||||||
if _current_test.assert_count == cur_assert_count and !_current_test.pending:
|
if _current_test.assert_count == cur_assert_count and !_current_test.pending:
|
||||||
_lgr.warn("Test did not assert")
|
_lgr.risky("Test did not assert")
|
||||||
|
|
||||||
_parameter_handler = null
|
_parameter_handler = null
|
||||||
|
|
||||||
|
@ -764,28 +648,10 @@ func _run_test(script_inst, test_name):
|
||||||
var script_result = null
|
var script_result = null
|
||||||
|
|
||||||
await script_inst.before_each()
|
await script_inst.before_each()
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
|
||||||
# var before_each_result = script_inst.before_each()
|
|
||||||
# if(_is_function_state(before_each_result)):
|
|
||||||
# await _wait_for_done(before_each_result)
|
|
||||||
# ----
|
|
||||||
|
|
||||||
start_test.emit(test_name)
|
start_test.emit(test_name)
|
||||||
|
|
||||||
await script_inst.call(test_name)
|
await script_inst.call(test_name)
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
|
||||||
# script_result = await script_inst.call(test_name)
|
|
||||||
# if(_is_function_state(script_result)):
|
|
||||||
# await _wait_for_done(script_result)
|
|
||||||
# ----
|
|
||||||
var test_summary = _new_summary.add_test(test_name)
|
|
||||||
if test_summary == null:
|
|
||||||
var msg = "Summary was null. This has been seen to happen when a test \n"
|
|
||||||
msg += "calls unreference. Adding 'await get_tree().process_frame' somewhere between\n"
|
|
||||||
msg += "instantiation and calling unreference, in your test, may fix this issue.\n"
|
|
||||||
msg += "More info at https://github.com/godotengine/godot/issues/69411"
|
|
||||||
_lgr.error(msg)
|
|
||||||
test_summary.force_a_runtime_error_to_stop_things_from_progressing_see_error_above = 1
|
|
||||||
|
|
||||||
# if the test called pause_before_teardown then await until
|
# if the test called pause_before_teardown then await until
|
||||||
# the continue button is pressed.
|
# the continue button is pressed.
|
||||||
|
@ -797,11 +663,6 @@ func _run_test(script_inst, test_name):
|
||||||
|
|
||||||
# call each post-each-test method until teardown is removed.
|
# call each post-each-test method until teardown is removed.
|
||||||
await script_inst.after_each()
|
await script_inst.after_each()
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
|
||||||
# var after_each_result = await script_inst.after_each()
|
|
||||||
# if(_is_function_state(after_each_result)):
|
|
||||||
# await _wait_for_done(after_each_result)
|
|
||||||
# ----
|
|
||||||
|
|
||||||
# Free up everything in the _autofree. Yield for a bit if we
|
# Free up everything in the _autofree. Yield for a bit if we
|
||||||
# have anything with a queue_free so that they have time to
|
# have anything with a queue_free so that they have time to
|
||||||
|
@ -809,9 +670,8 @@ func _run_test(script_inst, test_name):
|
||||||
var aqf_count = _autofree.get_queue_free_count()
|
var aqf_count = _autofree.get_queue_free_count()
|
||||||
_autofree.free_all()
|
_autofree.free_all()
|
||||||
if aqf_count > 0:
|
if aqf_count > 0:
|
||||||
await get_tree().create_timer(.25).timeout
|
await get_tree().create_timer(_auto_queue_free_delay).timeout
|
||||||
|
|
||||||
test_summary.orphans = _orphan_counter.get_counter("test")
|
|
||||||
if _log_level > 0:
|
if _log_level > 0:
|
||||||
_orphan_counter.print_orphans("test", _lgr)
|
_orphan_counter.print_orphans("test", _lgr)
|
||||||
|
|
||||||
|
@ -824,23 +684,22 @@ func _run_test(script_inst, test_name):
|
||||||
#
|
#
|
||||||
# Calls both pre-all-tests methods until prerun_setup is removed
|
# Calls both pre-all-tests methods until prerun_setup is removed
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _call_before_all(test_script):
|
func _call_before_all(test_script, collected_script):
|
||||||
_current_test = _before_all_test_obj
|
var before_all_test_obj = _utils.CollectedTest.new()
|
||||||
_current_test.has_printed_name = false
|
before_all_test_obj.has_printed_name = false
|
||||||
|
before_all_test_obj.name = "before_all"
|
||||||
|
|
||||||
|
collected_script.setup_teardown_tests.append(before_all_test_obj)
|
||||||
|
_current_test = before_all_test_obj
|
||||||
|
|
||||||
_lgr.inc_indent()
|
_lgr.inc_indent()
|
||||||
|
|
||||||
# Next 3 lines can be removed when prerun_setup removed.
|
|
||||||
_current_test.name = "prerun_setup"
|
|
||||||
_current_test.name = "before_all"
|
|
||||||
|
|
||||||
await test_script.before_all()
|
await test_script.before_all()
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
# before all does not need to assert anything so only mark it as run if
|
||||||
# var result = test_script.before_all()
|
# some assert was done.
|
||||||
# if(_is_function_state(result)):
|
before_all_test_obj.was_run = before_all_test_obj.did_something()
|
||||||
# await _wait_for_done(result)
|
|
||||||
# ----
|
|
||||||
|
|
||||||
_lgr.dec_indent()
|
_lgr.dec_indent()
|
||||||
|
|
||||||
_current_test = null
|
_current_test = null
|
||||||
|
|
||||||
|
|
||||||
|
@ -850,23 +709,21 @@ func _call_before_all(test_script):
|
||||||
#
|
#
|
||||||
# Calls both post-all-tests methods until postrun_teardown is removed.
|
# Calls both post-all-tests methods until postrun_teardown is removed.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _call_after_all(test_script):
|
func _call_after_all(test_script, collected_script):
|
||||||
_current_test = _after_all_test_obj
|
var after_all_test_obj = _utils.CollectedTest.new()
|
||||||
_current_test.has_printed_name = false
|
after_all_test_obj.has_printed_name = false
|
||||||
|
after_all_test_obj.name = "after_all"
|
||||||
|
|
||||||
|
collected_script.setup_teardown_tests.append(after_all_test_obj)
|
||||||
|
_current_test = after_all_test_obj
|
||||||
|
|
||||||
_lgr.inc_indent()
|
_lgr.inc_indent()
|
||||||
|
|
||||||
# Next 3 lines can be removed when postrun_teardown removed.
|
|
||||||
_current_test.name = "postrun_teardown"
|
|
||||||
_current_test.name = "after_all"
|
|
||||||
|
|
||||||
await test_script.after_all()
|
await test_script.after_all()
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
# after all does not need to assert anything so only mark it as run if
|
||||||
# var result = test_script.after_all()
|
# some assert was done.
|
||||||
# if(_is_function_state(result)):
|
after_all_test_obj.was_run = after_all_test_obj.did_something()
|
||||||
# await _wait_for_done(result)
|
|
||||||
# ----
|
|
||||||
|
|
||||||
_lgr.dec_indent()
|
_lgr.dec_indent()
|
||||||
|
|
||||||
_current_test = null
|
_current_test = null
|
||||||
|
|
||||||
|
|
||||||
|
@ -901,20 +758,19 @@ func _test_the_scripts(indexes = []):
|
||||||
|
|
||||||
# loop through scripts
|
# loop through scripts
|
||||||
for test_indexes in range(indexes_to_run.size()):
|
for test_indexes in range(indexes_to_run.size()):
|
||||||
var the_script = _test_collector.scripts[indexes_to_run[test_indexes]]
|
var coll_script = _test_collector.scripts[indexes_to_run[test_indexes]]
|
||||||
_orphan_counter.add_counter("script")
|
_orphan_counter.add_counter("script")
|
||||||
|
|
||||||
if the_script.tests.size() > 0:
|
if coll_script.tests.size() > 0:
|
||||||
_lgr.set_indent_level(0)
|
_lgr.set_indent_level(0)
|
||||||
_print_script_heading(the_script)
|
_print_script_heading(coll_script)
|
||||||
_new_summary.add_script(the_script.get_full_name())
|
|
||||||
|
|
||||||
if !the_script.is_loaded:
|
if !coll_script.is_loaded:
|
||||||
break
|
break
|
||||||
|
|
||||||
start_script.emit(the_script)
|
start_script.emit(coll_script)
|
||||||
|
|
||||||
var test_script = the_script.get_new()
|
var test_script = coll_script.get_new()
|
||||||
|
|
||||||
# ----
|
# ----
|
||||||
# SHORTCIRCUIT
|
# SHORTCIRCUIT
|
||||||
|
@ -925,8 +781,8 @@ func _test_the_scripts(indexes = []):
|
||||||
_lgr.inc_indent()
|
_lgr.inc_indent()
|
||||||
_lgr.log(msg, _lgr.fmts.yellow)
|
_lgr.log(msg, _lgr.fmts.yellow)
|
||||||
_lgr.dec_indent()
|
_lgr.dec_indent()
|
||||||
_new_summary.get_current_script().was_skipped = true
|
coll_script.skip_reason = skip_script
|
||||||
_new_summary.get_current_script().skip_reason = skip_script
|
coll_script.was_skipped = true
|
||||||
continue
|
continue
|
||||||
# ----
|
# ----
|
||||||
|
|
||||||
|
@ -939,32 +795,24 @@ func _test_the_scripts(indexes = []):
|
||||||
# inner class is set and we do not have a match then empty the tests
|
# inner class is set and we do not have a match then empty the tests
|
||||||
# for the current test.
|
# for the current test.
|
||||||
# !!!
|
# !!!
|
||||||
if !_does_class_name_match(_inner_class_name, the_script.inner_class_name):
|
if !_does_class_name_match(_inner_class_name, coll_script.inner_class_name):
|
||||||
the_script.tests = []
|
coll_script.tests = []
|
||||||
else:
|
else:
|
||||||
await _call_before_all(test_script)
|
coll_script.was_run = true
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
await _call_before_all(test_script, coll_script)
|
||||||
# var before_all_result = await _call_before_all(test_script)
|
|
||||||
# if(_is_function_state(before_all_result)):
|
|
||||||
# # _call_before_all calls _wait for done, just wait for that to finish
|
|
||||||
# await before_all_result.COMPLETED
|
|
||||||
# ----
|
|
||||||
|
|
||||||
# Each test in the script
|
# Each test in the script
|
||||||
var skip_suffix = "_skip__"
|
var skip_suffix = "_skip__"
|
||||||
the_script.mark_tests_to_skip_with_suffix(skip_suffix)
|
coll_script.mark_tests_to_skip_with_suffix(skip_suffix)
|
||||||
for i in range(the_script.tests.size()):
|
for i in range(coll_script.tests.size()):
|
||||||
_stubber.clear()
|
_stubber.clear()
|
||||||
_spy.clear()
|
_spy.clear()
|
||||||
_current_test = the_script.tests[i]
|
_current_test = coll_script.tests[i]
|
||||||
script_result = null
|
script_result = null
|
||||||
|
|
||||||
# ------------------
|
# ------------------
|
||||||
# SHORTCIRCUI
|
# SHORTCIRCUI
|
||||||
if _current_test.should_skip:
|
if _current_test.should_skip:
|
||||||
_new_summary.add_pending(
|
|
||||||
_current_test.name, "SKIPPED because it ends with " + skip_suffix
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
# ------------------
|
# ------------------
|
||||||
|
|
||||||
|
@ -983,18 +831,14 @@ func _test_the_scripts(indexes = []):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif _current_test.arg_count == 1:
|
elif _current_test.arg_count == 1:
|
||||||
|
_current_test.was_run = true
|
||||||
script_result = await _run_parameterized_test(test_script, _current_test.name)
|
script_result = await _run_parameterized_test(test_script, _current_test.name)
|
||||||
else:
|
else:
|
||||||
|
_current_test.was_run = true
|
||||||
script_result = await _run_test(test_script, _current_test.name)
|
script_result = await _run_test(test_script, _current_test.name)
|
||||||
|
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
if !_current_test.did_something():
|
||||||
# if(_is_function_state(script_result)):
|
_lgr.risky(str(_current_test.name, " did not assert"))
|
||||||
# # _run_test calls _wait for done, just wait for that to finish
|
|
||||||
# await script_result.COMPLETED
|
|
||||||
# ----
|
|
||||||
|
|
||||||
if !_current_test.did_assert():
|
|
||||||
_lgr.warn("Test did not assert")
|
|
||||||
|
|
||||||
_current_test.has_printed_name = false
|
_current_test.has_printed_name = false
|
||||||
end_test.emit()
|
end_test.emit()
|
||||||
|
@ -1012,14 +856,8 @@ func _test_the_scripts(indexes = []):
|
||||||
_lgr.dec_indent()
|
_lgr.dec_indent()
|
||||||
_orphan_counter.print_orphans("script", _lgr)
|
_orphan_counter.print_orphans("script", _lgr)
|
||||||
|
|
||||||
if _does_class_name_match(_inner_class_name, the_script.inner_class_name):
|
if _does_class_name_match(_inner_class_name, coll_script.inner_class_name):
|
||||||
await _call_after_all(test_script)
|
await _call_after_all(test_script, coll_script)
|
||||||
# TODO 4.0 GDScriptFunctionState? ----
|
|
||||||
# var after_all_result = await _call_after_all(test_script)
|
|
||||||
# if(_is_function_state(after_all_result)):
|
|
||||||
# # _call_after_all calls _wait for done, just wait for that to finish
|
|
||||||
# await after_all_result.COMPLETED
|
|
||||||
# ----
|
|
||||||
|
|
||||||
_log_test_children_warning(test_script)
|
_log_test_children_warning(test_script)
|
||||||
# This might end up being very resource intensive if the scripts
|
# This might end up being very resource intensive if the scripts
|
||||||
|
@ -1031,7 +869,10 @@ func _test_the_scripts(indexes = []):
|
||||||
_lgr.set_indent_level(0)
|
_lgr.set_indent_level(0)
|
||||||
if test_script.get_assert_count() > 0:
|
if test_script.get_assert_count() > 0:
|
||||||
var script_sum = str(
|
var script_sum = str(
|
||||||
test_script.get_pass_count(), "/", test_script.get_assert_count(), " passed."
|
coll_script.get_passing_test_count(),
|
||||||
|
"/",
|
||||||
|
coll_script.get_ran_test_count(),
|
||||||
|
" passed."
|
||||||
)
|
)
|
||||||
_lgr.log(script_sum, _lgr.fmts.bold)
|
_lgr.log(script_sum, _lgr.fmts.bold)
|
||||||
|
|
||||||
|
@ -1046,11 +887,22 @@ func _test_the_scripts(indexes = []):
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _pass(text = ""):
|
func _pass(text = ""):
|
||||||
if _current_test:
|
if _current_test:
|
||||||
_current_test.assert_count += 1
|
_current_test.add_pass(text)
|
||||||
_new_summary.add_pass(_current_test.name, text)
|
|
||||||
else:
|
|
||||||
if _new_summary != null: # b/c of tests.
|
# ------------------------------------------------------------------------------
|
||||||
_new_summary.add_pass("script level", text)
|
# Returns an empty string or "(call #x) " if the current test being run has
|
||||||
|
# parameters. The
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
func get_call_count_text():
|
||||||
|
var to_return = ""
|
||||||
|
if _parameter_handler != null:
|
||||||
|
# This uses get_call_count -1 because test.gd's use_parameters method
|
||||||
|
# should have been called before we get to any calls for this method
|
||||||
|
# just due to how use_parameters works. There isn't a way to know
|
||||||
|
# whether we are before or after that call.
|
||||||
|
to_return = str("params[", _parameter_handler.get_call_count() - 1, "] ")
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -1062,16 +914,26 @@ func _fail(text = ""):
|
||||||
p(line_text, LOG_LEVEL_FAIL_ONLY)
|
p(line_text, LOG_LEVEL_FAIL_ONLY)
|
||||||
# format for summary
|
# format for summary
|
||||||
line_text = "\n " + line_text
|
line_text = "\n " + line_text
|
||||||
var call_count_text = ""
|
var call_count_text = get_call_count_text()
|
||||||
if _parameter_handler != null:
|
|
||||||
call_count_text = str("(call #", _parameter_handler.get_call_count(), ") ")
|
|
||||||
_new_summary.add_fail(_current_test.name, call_count_text + text + line_text)
|
|
||||||
_current_test.passed = false
|
|
||||||
_current_test.assert_count += 1
|
|
||||||
_current_test.line_number = line_number
|
_current_test.line_number = line_number
|
||||||
else:
|
_current_test.add_fail(call_count_text + text + line_text)
|
||||||
if _new_summary != null: # b/c of tests.
|
|
||||||
_new_summary.add_fail("script level", text)
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# This is "private" but is only used by the logger, it is not used internally.
|
||||||
|
# It was either, make this weird method or "do it the right way" with signals
|
||||||
|
# or some other crazy mechanism.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
func _fail_for_error(err_text):
|
||||||
|
if _current_test != null and treat_error_as_failure:
|
||||||
|
_fail(err_text)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
func _pending(text = ""):
|
||||||
|
if _current_test:
|
||||||
|
_current_test.add_pending(text)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -1090,14 +952,6 @@ func _extract_line_number(current_test):
|
||||||
return line_number
|
return line_number
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
func _pending(text = ""):
|
|
||||||
if _current_test:
|
|
||||||
_current_test.pending = true
|
|
||||||
_new_summary.add_pending(_current_test.name, text)
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Gets all the files in a directory and all subdirectories if include_subdirectories
|
# Gets all the files in a directory and all subdirectories if include_subdirectories
|
||||||
# is true. The files returned are all sorted by name.
|
# is true. The files returned are all sorted by name.
|
||||||
|
@ -1166,7 +1020,7 @@ func get_elapsed_time():
|
||||||
func p(text, level = 0):
|
func p(text, level = 0):
|
||||||
var str_text = str(text)
|
var str_text = str(text)
|
||||||
|
|
||||||
if level <= _utils.nvl(_log_level, 0):
|
if level <= GutUtils.nvl(_log_level, 0):
|
||||||
_lgr.log(str_text)
|
_lgr.log(str_text)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1181,8 +1035,6 @@ func p(text, level = 0):
|
||||||
# Runs all the scripts that were added using add_script
|
# Runs all the scripts that were added using add_script
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func test_scripts(run_rest = false):
|
func test_scripts(run_rest = false):
|
||||||
clear_text()
|
|
||||||
|
|
||||||
if _script_name != null and _script_name != "":
|
if _script_name != null and _script_name != "":
|
||||||
var indexes = _get_indexes_matching_script_name(_script_name)
|
var indexes = _get_indexes_matching_script_name(_script_name)
|
||||||
if indexes == []:
|
if indexes == []:
|
||||||
|
@ -1286,8 +1138,8 @@ func import_tests(path = _export_path):
|
||||||
_test_collector.clear()
|
_test_collector.clear()
|
||||||
var result = _test_collector.import_tests(path)
|
var result = _test_collector.import_tests(path)
|
||||||
if result:
|
if result:
|
||||||
_lgr.info(_test_collector.to_s())
|
_lgr.info("\n" + _test_collector.to_s())
|
||||||
_lgr.info("Importd from " + path)
|
_lgr.info("Imported from " + path)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -1328,36 +1180,35 @@ func clear_text():
|
||||||
# Get the number of tests that were ran
|
# Get the number of tests that were ran
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func get_test_count():
|
func get_test_count():
|
||||||
return _new_summary.get_totals().tests
|
return _test_collector.get_ran_test_count()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Get the number of assertions that were made
|
# Get the number of assertions that were made
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func get_assert_count():
|
func get_assert_count():
|
||||||
var t = _new_summary.get_totals()
|
return _test_collector.get_assert_count()
|
||||||
return t.passing + t.failing
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Get the number of assertions that passed
|
# Get the number of assertions that passed
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func get_pass_count():
|
func get_pass_count():
|
||||||
return _new_summary.get_totals().passing
|
return _test_collector.get_pass_count()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Get the number of assertions that failed
|
# Get the number of assertions that failed
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func get_fail_count():
|
func get_fail_count():
|
||||||
return _new_summary.get_totals().failing
|
return _test_collector.get_fail_count()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Get the number of tests flagged as pending
|
# Get the number of tests flagged as pending
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func get_pending_count():
|
func get_pending_count():
|
||||||
return _new_summary.get_totals().pending
|
return _test_collector.get_pending_count()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -1417,7 +1268,7 @@ func get_current_test_object():
|
||||||
## Returns a summary.gd object that contains all the information about
|
## Returns a summary.gd object that contains all the information about
|
||||||
## the run results.
|
## the run results.
|
||||||
func get_summary():
|
func get_summary():
|
||||||
return _new_summary
|
return _utils.Summary.new(self)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -1436,3 +1287,33 @@ func get_post_run_script_instance():
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func show_orphans(should):
|
func show_orphans(should):
|
||||||
_lgr.set_type_enabled(_lgr.types.orphan, should)
|
_lgr.set_type_enabled(_lgr.types.orphan, should)
|
||||||
|
|
||||||
|
|
||||||
|
func get_logger():
|
||||||
|
return _lgr
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
# The MIT License (MIT)
|
||||||
|
# =====================
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 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.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
|
|
|
@ -1,31 +1,4 @@
|
||||||
# ##############################################################################
|
# ------------------------------------------------------------------------------
|
||||||
#(G)odot (U)nit (T)est class
|
|
||||||
#
|
|
||||||
# ##############################################################################
|
|
||||||
# The MIT License (MIT)
|
|
||||||
# =====================
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020 Tom "Butch" Wesley
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
#
|
|
||||||
# ##############################################################################
|
|
||||||
# Description
|
# Description
|
||||||
# -----------
|
# -----------
|
||||||
# Command line interface for the GUT unit testing tool. Allows you to run tests
|
# Command line interface for the GUT unit testing tool. Allows you to run tests
|
||||||
|
@ -36,7 +9,7 @@
|
||||||
#
|
#
|
||||||
# See the readme for a list of options and examples. You can also use the -gh
|
# 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.
|
# option to get more information about how to use the command line interface.
|
||||||
# ##############################################################################
|
# ------------------------------------------------------------------------------
|
||||||
extends SceneTree
|
extends SceneTree
|
||||||
|
|
||||||
var Optparse = load("res://addons/gut/optparse.gd")
|
var Optparse = load("res://addons/gut/optparse.gd")
|
||||||
|
@ -44,7 +17,6 @@ var Gut = load("res://addons/gut/gut.gd")
|
||||||
var GutRunner = load("res://addons/gut/gui/GutRunner.tscn")
|
var GutRunner = load("res://addons/gut/gui/GutRunner.tscn")
|
||||||
|
|
||||||
var json = JSON.new()
|
var json = JSON.new()
|
||||||
var exit_code = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -128,7 +100,7 @@ class OptionResolver:
|
||||||
# Here starts the actual script that uses the Options class to kick off Gut
|
# Here starts the actual script that uses the Options class to kick off Gut
|
||||||
# and run your tests.
|
# and run your tests.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
var _utils = null
|
||||||
var _gut_config = load("res://addons/gut/gut_config.gd").new()
|
var _gut_config = load("res://addons/gut/gut_config.gd").new()
|
||||||
# instance of gut
|
# instance of gut
|
||||||
var _tester = null
|
var _tester = null
|
||||||
|
@ -138,19 +110,47 @@ var _final_opts = []
|
||||||
|
|
||||||
func setup_options(options, font_names):
|
func setup_options(options, font_names):
|
||||||
var opts = Optparse.new()
|
var opts = Optparse.new()
|
||||||
opts.set_banner(
|
|
||||||
(
|
(
|
||||||
"This is the command line interface for the unit testing tool Gut. With this "
|
opts
|
||||||
+ "interface you can run one or more test scripts from the command line. In order "
|
. set_banner(
|
||||||
+ "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 '
|
The GUT CLI
|
||||||
+ '"-g<name>=<value>". 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."
|
The default behavior for GUT is to load options from a res://.gutconfig.json if
|
||||||
|
it exists. Any options specified on the command line will take precedence over
|
||||||
|
options specified in the gutconfig file. You can specify a different gutconfig
|
||||||
|
file with the -gconfig option.
|
||||||
|
|
||||||
|
To generate a .gutconfig.json file you can use -gprint_gutconfig_sample
|
||||||
|
To see the effective values of a CLI command and a gutconfig use -gpo
|
||||||
|
|
||||||
|
Any option that requires a value will take the form of \"-g<name>=<value>\".
|
||||||
|
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.
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# Run specific things
|
||||||
|
opts.add(
|
||||||
|
"-gselect",
|
||||||
|
"",
|
||||||
|
"All scripts that contain the specified string in their filename will be ran"
|
||||||
|
)
|
||||||
|
opts.add(
|
||||||
|
"-ginner_class",
|
||||||
|
"",
|
||||||
|
"Only run inner classes that contain the specified string int their name."
|
||||||
|
)
|
||||||
|
opts.add(
|
||||||
|
"-gunit_test_name",
|
||||||
|
"",
|
||||||
|
"Any test that contains the specified text will be run, all others will be skipped."
|
||||||
|
)
|
||||||
|
|
||||||
opts.add("-gtest", [], "Comma delimited list of full paths to test scripts to run.")
|
# Run Config
|
||||||
|
opts.add("-ginclude_subdirs", false, "Include subdirectories of -gdir.")
|
||||||
opts.add("-gdir", options.dirs, "Comma delimited list of directories to add tests from.")
|
opts.add("-gdir", options.dirs, "Comma delimited list of directories to add tests from.")
|
||||||
|
opts.add("-gtest", [], "Comma delimited list of full paths to test scripts to run.")
|
||||||
opts.add(
|
opts.add(
|
||||||
"-gprefix",
|
"-gprefix",
|
||||||
options.prefix,
|
options.prefix,
|
||||||
|
@ -161,6 +161,39 @@ func setup_options(options, font_names):
|
||||||
options.suffix,
|
options.suffix,
|
||||||
'Test script suffix, including .gd extension. Default "[default]".'
|
'Test script suffix, including .gd extension. Default "[default]".'
|
||||||
)
|
)
|
||||||
|
opts.add(
|
||||||
|
"-gconfig",
|
||||||
|
"res://.gutconfig.json",
|
||||||
|
"A config file that contains configuration information. Default is res://.gutconfig.json"
|
||||||
|
)
|
||||||
|
opts.add("-gpre_run_script", "", "pre-run hook script path")
|
||||||
|
opts.add("-gpost_run_script", "", "post-run hook script path")
|
||||||
|
(
|
||||||
|
opts
|
||||||
|
. add(
|
||||||
|
"-gerrors_do_not_cause_failure",
|
||||||
|
false,
|
||||||
|
"When an internal GUT error occurs tests will fail. With this option set, that does not happen."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
opts
|
||||||
|
. add(
|
||||||
|
"-gdouble_strategy",
|
||||||
|
"SCRIPT_ONLY",
|
||||||
|
'Default strategy to use when doubling. Valid values are [INCLUDE_NATIVE, SCRIPT_ONLY]. Default "[default]"'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
opts.add(
|
||||||
|
"-gpaint_after",
|
||||||
|
options.paint_after,
|
||||||
|
"Delay before GUT will add a 1 frame pause to paint the screen/GUI. default [default]"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Display options
|
||||||
|
opts.add("-glog", options.log_level, "Log level. Default [default]")
|
||||||
opts.add(
|
opts.add(
|
||||||
"-ghide_orphans",
|
"-ghide_orphans",
|
||||||
false,
|
false,
|
||||||
|
@ -170,63 +203,12 @@ func setup_options(options, font_names):
|
||||||
opts.add(
|
opts.add(
|
||||||
"-gcompact_mode", false, "The runner will be in compact mode. This overrides -gmaximize."
|
"-gcompact_mode", false, "The runner will be in compact mode. This overrides -gmaximize."
|
||||||
)
|
)
|
||||||
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", options.log_level, "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(
|
opts.add(
|
||||||
"-gopacity",
|
"-gopacity",
|
||||||
options.opacity,
|
options.opacity,
|
||||||
"Set opacity of test runner window. Use range 0 - 100. 0 = transparent, 100 = opaque."
|
"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("-gdisable_colors", false, "Disable command line colors.")
|
opts.add("-gdisable_colors", false, "Disable command line colors.")
|
||||||
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."
|
|
||||||
)
|
|
||||||
|
|
||||||
opts.add(
|
opts.add(
|
||||||
"-gfont_name",
|
"-gfont_name",
|
||||||
options.font_name,
|
options.font_name,
|
||||||
|
@ -239,12 +221,28 @@ func setup_options(options, font_names):
|
||||||
'Background color as an html color, default "[default]"'
|
'Background color as an html color, default "[default]"'
|
||||||
)
|
)
|
||||||
opts.add("-gfont_color", options.font_color, 'Font color as an html color, default "[default]"')
|
opts.add("-gfont_color", options.font_color, 'Font color as an html color, default "[default]"')
|
||||||
|
|
||||||
|
# End Behavior
|
||||||
opts.add(
|
opts.add(
|
||||||
"-gpaint_after",
|
"-gexit",
|
||||||
options.paint_after,
|
false,
|
||||||
"Delay before GUT will add a 1 frame pause to paint the screen/GUI. default [default]"
|
"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("-gignore_pause", false, "Ignores any calls to gut.pause_before_teardown.")
|
||||||
|
|
||||||
|
# Helpish options
|
||||||
|
opts.add(
|
||||||
|
"-gh", false, "Print this help. You did this to see this, so you probably understand."
|
||||||
|
)
|
||||||
|
opts.add("-gpo", false, "Print option values from all sources and the value used.")
|
||||||
|
opts.add(
|
||||||
|
"-gprint_gutconfig_sample",
|
||||||
|
false,
|
||||||
|
"Print out json that can be used to make a gutconfig file."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Output options
|
||||||
opts.add(
|
opts.add(
|
||||||
"-gjunit_xml_file",
|
"-gjunit_xml_file",
|
||||||
options.junit_xml_file,
|
options.junit_xml_file,
|
||||||
|
@ -255,6 +253,7 @@ func setup_options(options, font_names):
|
||||||
options.junit_xml_timestamp,
|
options.junit_xml_timestamp,
|
||||||
"Include a timestamp in the -gjunit_xml_file, default [default]"
|
"Include a timestamp in the -gjunit_xml_file, default [default]"
|
||||||
)
|
)
|
||||||
|
|
||||||
return opts
|
return opts
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,6 +279,7 @@ func extract_command_line_options(from, to):
|
||||||
to.compact_mode = from.get_value("-gcompact_mode")
|
to.compact_mode = from.get_value("-gcompact_mode")
|
||||||
to.hide_orphans = from.get_value("-ghide_orphans")
|
to.hide_orphans = from.get_value("-ghide_orphans")
|
||||||
to.suffix = from.get_value("-gsuffix")
|
to.suffix = from.get_value("-gsuffix")
|
||||||
|
to.errors_do_not_cause_failure = from.get_value("-gerrors_do_not_cause_failure")
|
||||||
to.tests = from.get_value("-gtest")
|
to.tests = from.get_value("-gtest")
|
||||||
to.unit_test_name = from.get_value("-gunit_test_name")
|
to.unit_test_name = from.get_value("-gunit_test_name")
|
||||||
|
|
||||||
|
@ -294,10 +294,10 @@ func extract_command_line_options(from, to):
|
||||||
|
|
||||||
|
|
||||||
func _print_gutconfigs(values):
|
func _print_gutconfigs(values):
|
||||||
var header = """Here is a sample of a full super.gutconfig.json file.
|
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
|
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
|
this sample are what would be used if you ran gut w/o the -gprint_gutconfig_sample
|
||||||
option (option priority: command-line, super.gutconfig, default)."""
|
option (option priority: command-line, .gutconfig, default)."""
|
||||||
print("\n", header.replace("\n", " "), "\n\n")
|
print("\n", header.replace("\n", " "), "\n\n")
|
||||||
var resolved = values
|
var resolved = values
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ func _run_gut():
|
||||||
(
|
(
|
||||||
"All command line options and where they are specified. "
|
"All command line options and where they are specified. "
|
||||||
+ 'The "final" value shows which value will actually be used '
|
+ 'The "final" value shows which value will actually be used '
|
||||||
+ "based on order of precedence (default < super.gutconfig < cmd line)."
|
+ "based on order of precedence (default < .gutconfig < cmd line)."
|
||||||
+ "\n"
|
+ "\n"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -379,21 +379,22 @@ func _run_gut():
|
||||||
func _on_tests_finished(should_exit, should_exit_on_success):
|
func _on_tests_finished(should_exit, should_exit_on_success):
|
||||||
if _final_opts.dirs.size() == 0:
|
if _final_opts.dirs.size() == 0:
|
||||||
if _tester.get_summary().get_totals().scripts == 0:
|
if _tester.get_summary().get_totals().scripts == 0:
|
||||||
var lgr = _tester.get_logger()
|
var lgr = _tester.logger
|
||||||
(
|
(
|
||||||
lgr
|
lgr
|
||||||
. error(
|
. error(
|
||||||
"No directories configured. Add directories with options or a super.gutconfig.json file. Use the -gh option for more information."
|
"No directories configured. Add directories with options or a .gutconfig.json file. Use the -gh option for more information."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var exit_code = 0
|
||||||
if _tester.get_fail_count():
|
if _tester.get_fail_count():
|
||||||
set_exit_code(1)
|
exit_code = 1
|
||||||
|
|
||||||
# Overwrite the exit code with the post_script
|
# Overwrite the exit code with the post_script
|
||||||
var post_inst = _tester.get_post_run_script_instance()
|
var post_inst = _tester.get_post_run_script_instance()
|
||||||
if post_inst != null and post_inst.get_exit_code() != null:
|
if post_inst != null and post_inst.get_exit_code() != null:
|
||||||
set_exit_code(post_inst.get_exit_code())
|
exit_code = post_inst.get_exit_code()
|
||||||
|
|
||||||
if should_exit or (should_exit_on_success and _tester.get_fail_count() == 0):
|
if should_exit or (should_exit_on_success and _tester.get_fail_count() == 0):
|
||||||
quit(exit_code)
|
quit(exit_code)
|
||||||
|
@ -401,18 +402,56 @@ func _on_tests_finished(should_exit, should_exit_on_success):
|
||||||
print("Tests finished, exit manually")
|
print("Tests finished, exit manually")
|
||||||
|
|
||||||
|
|
||||||
func set_exit_code(val):
|
|
||||||
exit_code = val
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# MAIN
|
# MAIN
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _init():
|
func _init():
|
||||||
|
var max_iter = 20
|
||||||
|
var iter = 0
|
||||||
|
|
||||||
|
# Not seen this wait more than 1.
|
||||||
|
while Engine.get_main_loop() == null and iter < max_iter:
|
||||||
|
await create_timer(.01).timeout
|
||||||
|
iter += 1
|
||||||
|
|
||||||
|
if Engine.get_main_loop() == null:
|
||||||
|
push_error("Main loop did not start in time.")
|
||||||
|
quit(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
_utils = GutUtils.get_instance()
|
||||||
if !_utils.is_version_ok():
|
if !_utils.is_version_ok():
|
||||||
print("\n\n", _utils.get_version_text())
|
print("\n\n", _utils.get_version_text())
|
||||||
push_error(_utils.get_bad_version_text())
|
push_error(_utils.get_bad_version_text())
|
||||||
set_exit_code(1)
|
quit(1)
|
||||||
quit()
|
|
||||||
else:
|
else:
|
||||||
_run_gut()
|
_run_gut()
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
#(G)odot (U)nit (T)est class
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
|
# The MIT License (MIT)
|
||||||
|
# =====================
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 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.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
|
# ##############################################################################
|
||||||
|
#
|
||||||
|
# This holds all the configuratoin values for GUT. It can load and save values
|
||||||
|
# to a json file. It is also responsible for applying these settings to GUT.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
var Gut = load("res://addons/gut/gut.gd")
|
var Gut = load("res://addons/gut/gut.gd")
|
||||||
|
|
||||||
# Do not want a ref to _utils here due to use by editor plugin.
|
|
||||||
# _utils needs to be split so that constants and what not do not
|
|
||||||
# have to rely on the weird singleton thing I made.
|
|
||||||
enum DOUBLE_STRATEGY { FULL, PARTIAL }
|
|
||||||
|
|
||||||
var valid_fonts = ["AnonymousPro", "CourierPro", "LobsterTwo", "Default"]
|
var valid_fonts = ["AnonymousPro", "CourierPro", "LobsterTwo", "Default"]
|
||||||
var default_options = {
|
var default_options = {
|
||||||
background_color = Color(.15, .15, .15, 1).to_html(),
|
background_color = Color(.15, .15, .15, 1).to_html(),
|
||||||
config_file = "res://.gutconfig.json",
|
config_file = "res://.gutconfig.json",
|
||||||
dirs = [],
|
dirs = [],
|
||||||
disable_colors = false,
|
disable_colors = false,
|
||||||
double_strategy = "partial",
|
# double strategy can be the name of the enum value, the enum value or
|
||||||
|
# lowercase name with spaces: 0/SCRIPT_ONLY/script only
|
||||||
|
# The GUI gut config expects the value to be the enum value and not a string
|
||||||
|
# when saved.
|
||||||
|
double_strategy = "SCRIPT_ONLY",
|
||||||
|
# named differently than gut option so we can use it as a flag in the cli
|
||||||
|
errors_do_not_cause_failure = false,
|
||||||
font_color = Color(.8, .8, .8, 1).to_html(),
|
font_color = Color(.8, .8, .8, 1).to_html(),
|
||||||
font_name = "CourierPrime",
|
font_name = "CourierPrime",
|
||||||
font_size = 16,
|
font_size = 16,
|
||||||
|
@ -41,7 +48,9 @@ var default_options = {
|
||||||
|
|
||||||
var default_panel_options = {
|
var default_panel_options = {
|
||||||
font_name = "CourierPrime",
|
font_name = "CourierPrime",
|
||||||
font_size = 30,
|
font_size = 16,
|
||||||
|
output_font_name = "CourierPrime",
|
||||||
|
output_font_size = 30,
|
||||||
hide_result_tree = false,
|
hide_result_tree = false,
|
||||||
hide_output_text = false,
|
hide_output_text = false,
|
||||||
hide_settings = false,
|
hide_settings = false,
|
||||||
|
@ -61,7 +70,6 @@ func _null_copy(h):
|
||||||
|
|
||||||
func _load_options_from_config_file(file_path, into):
|
func _load_options_from_config_file(file_path, into):
|
||||||
# SHORTCIRCUIT
|
# SHORTCIRCUIT
|
||||||
|
|
||||||
if !FileAccess.file_exists(file_path):
|
if !FileAccess.file_exists(file_path):
|
||||||
if file_path != "res://.gutconfig.json":
|
if file_path != "res://.gutconfig.json":
|
||||||
print('ERROR: Config File "', file_path, '" does not exist.')
|
print('ERROR: Config File "', file_path, '" does not exist.')
|
||||||
|
@ -105,6 +113,45 @@ func _load_dict_into(source, dest):
|
||||||
dest[key] = source[key]
|
dest[key] = source[key]
|
||||||
|
|
||||||
|
|
||||||
|
# Apply all the options specified to tester. This is where the rubber meets
|
||||||
|
# the road.
|
||||||
|
func _apply_options(opts, gut):
|
||||||
|
gut.include_subdirectories = opts.include_subdirs
|
||||||
|
|
||||||
|
if opts.inner_class != "":
|
||||||
|
gut.inner_class_name = opts.inner_class
|
||||||
|
gut.log_level = opts.log_level
|
||||||
|
gut.ignore_pause_before_teardown = opts.ignore_pause
|
||||||
|
|
||||||
|
gut.select_script(opts.selected)
|
||||||
|
|
||||||
|
for i in range(opts.dirs.size()):
|
||||||
|
gut.add_directory(opts.dirs[i], opts.prefix, opts.suffix)
|
||||||
|
|
||||||
|
for i in range(opts.tests.size()):
|
||||||
|
gut.add_script(opts.tests[i])
|
||||||
|
|
||||||
|
# Sometimes it is the index, sometimes it's a string. This sets it regardless
|
||||||
|
gut.double_strategy = GutUtils.get_enum_value(
|
||||||
|
opts.double_strategy, GutUtils.DOUBLE_STRATEGY, GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE
|
||||||
|
)
|
||||||
|
|
||||||
|
gut.unit_test_name = opts.unit_test_name
|
||||||
|
gut.pre_run_script = opts.pre_run_script
|
||||||
|
gut.post_run_script = opts.post_run_script
|
||||||
|
gut.color_output = !opts.disable_colors
|
||||||
|
gut.show_orphans(!opts.hide_orphans)
|
||||||
|
gut.junit_xml_file = opts.junit_xml_file
|
||||||
|
gut.junit_xml_timestamp = opts.junit_xml_timestamp
|
||||||
|
gut.paint_after = str(opts.paint_after).to_float()
|
||||||
|
gut.treat_error_as_failure = !opts.errors_do_not_cause_failure
|
||||||
|
|
||||||
|
return gut
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------
|
||||||
|
# Public
|
||||||
|
# --------------------------
|
||||||
func write_options(path):
|
func write_options(path):
|
||||||
var content = json.stringify(options, " ")
|
var content = json.stringify(options, " ")
|
||||||
|
|
||||||
|
@ -112,52 +159,12 @@ func write_options(path):
|
||||||
var result = FileAccess.get_open_error()
|
var result = FileAccess.get_open_error()
|
||||||
if f != null:
|
if f != null:
|
||||||
f.store_string(content)
|
f.store_string(content)
|
||||||
f.close()
|
f = null # closes file
|
||||||
else:
|
else:
|
||||||
print("ERROR: could not open file ", path, " ", result)
|
print("ERROR: could not open file ", path, " ", result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
# Apply all the options specified to _tester. This is where the rubber meets
|
|
||||||
# the road.
|
|
||||||
func _apply_options(opts, _tester):
|
|
||||||
_tester.include_subdirectories = opts.include_subdirs
|
|
||||||
|
|
||||||
if opts.inner_class != "":
|
|
||||||
_tester.inner_class_name = opts.inner_class
|
|
||||||
_tester.log_level = opts.log_level
|
|
||||||
_tester.ignore_pause_before_teardown = opts.ignore_pause
|
|
||||||
|
|
||||||
if opts.selected != "":
|
|
||||||
_tester.select_script(opts.selected)
|
|
||||||
|
|
||||||
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.double_strategy == "include super":
|
|
||||||
_tester.double_strategy = DOUBLE_STRATEGY.FULL
|
|
||||||
elif opts.double_strategy == "script only":
|
|
||||||
_tester.double_strategy = DOUBLE_STRATEGY.PARTIAL
|
|
||||||
|
|
||||||
_tester.unit_test_name = opts.unit_test_name
|
|
||||||
_tester.pre_run_script = opts.pre_run_script
|
|
||||||
_tester.post_run_script = opts.post_run_script
|
|
||||||
_tester.color_output = !opts.disable_colors
|
|
||||||
_tester.show_orphans(!opts.hide_orphans)
|
|
||||||
_tester.junit_xml_file = opts.junit_xml_file
|
|
||||||
_tester.junit_xml_timestamp = opts.junit_xml_timestamp
|
|
||||||
_tester.paint_after = str(opts.paint_after).to_float()
|
|
||||||
|
|
||||||
return _tester
|
|
||||||
|
|
||||||
|
|
||||||
func config_gut(gut):
|
|
||||||
return _apply_options(options, gut)
|
|
||||||
|
|
||||||
|
|
||||||
func load_options(path):
|
func load_options(path):
|
||||||
return _load_options_from_config_file(path, options)
|
return _load_options_from_config_file(path, options)
|
||||||
|
|
||||||
|
@ -174,3 +181,29 @@ func load_options_no_defaults(path):
|
||||||
|
|
||||||
func apply_options(gut):
|
func apply_options(gut):
|
||||||
_apply_options(options, gut)
|
_apply_options(options, gut)
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
# The MIT License (MIT)
|
||||||
|
# =====================
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 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.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
|
|
|
@ -66,16 +66,25 @@ func file_touch(path):
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Call _process or _fixed_process, if they exist, on obj and all it's children
|
# Simulate a number of frames by calling '_process' and '_physics_process' (if
|
||||||
# and their children and so and so forth. Delta will be passed through to all
|
# the methods exist) on an object and all of its descendents. The specified frame
|
||||||
# the _process or _fixed_process methods.
|
# time, 'delta', will be passed to each simulated call.
|
||||||
|
#
|
||||||
|
# NOTE: Objects can disable their processing methods using 'set_process(false)' and
|
||||||
|
# 'set_physics_process(false)'. This is reflected in the 'Object' methods
|
||||||
|
# 'is_processing()' and 'is_physics_processing()', respectively. To make 'simulate'
|
||||||
|
# respect this status, for example if you are testing an object which toggles
|
||||||
|
# processing, pass 'check_is_processing' as 'true'.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func simulate(obj, times, delta):
|
func simulate(obj, times, delta, check_is_processing: bool = false):
|
||||||
for _i in range(times):
|
for _i in range(times):
|
||||||
if obj.has_method("_process"):
|
if obj.has_method("_process") and (not check_is_processing or obj.is_processing()):
|
||||||
obj._process(delta)
|
obj._process(delta)
|
||||||
if obj.has_method("_physics_process"):
|
if (
|
||||||
|
obj.has_method("_physics_process")
|
||||||
|
and (not check_is_processing or obj.is_physics_processing())
|
||||||
|
):
|
||||||
obj._physics_process(delta)
|
obj._physics_process(delta)
|
||||||
|
|
||||||
for kid in obj.get_children():
|
for kid in obj.get_children():
|
||||||
simulate(kid, 1, delta)
|
simulate(kid, 1, delta, check_is_processing)
|
||||||
|
|
|
@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/icon.png-91b084043b8aaf2f1c906e7b9fa92969.cte
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
compress/mode=0
|
compress/mode=0
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
compress/lossy_quality=0.7
|
||||||
compress/hdr_compression=1
|
compress/hdr_compression=1
|
||||||
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
compress/channel_pack=0
|
compress/channel_pack=0
|
||||||
mipmaps/generate=false
|
mipmaps/generate=false
|
||||||
|
|
1
addons/gut/images/Folder.svg
Normal file
1
addons/gut/images/Folder.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2a1 1 0 0 0 -1 1v2 6 2a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-7a1 1 0 0 0 -1-1h-4a1 1 0 0 1 -1-1v-1a1 1 0 0 0 -1-1z" fill="#e0e0e0"/></svg>
|
After Width: | Height: | Size: 227 B |
37
addons/gut/images/Folder.svg.import
Normal file
37
addons/gut/images/Folder.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dhvl14hls3y2j"
|
||||||
|
path="res://.godot/imported/Folder.svg-caa50e6a0be9d456fd81991dfb537916.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/gut/images/Folder.svg"
|
||||||
|
dest_files=["res://.godot/imported/Folder.svg-caa50e6a0be9d456fd81991dfb537916.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
1
addons/gut/images/Script.svg
Normal file
1
addons/gut/images/Script.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1a1 1 0 0 0 -1 1v10h-1v-2h-2v2a1 1 0 0 0 .5.86523 1 1 0 0 0 .5.13477v1h7a2 2 0 0 0 2-2v-8h3v-2a2 2 0 0 0 -2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b3b3b3" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/></g></svg>
|
After Width: | Height: | Size: 606 B |
37
addons/gut/images/Script.svg.import
Normal file
37
addons/gut/images/Script.svg.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cavojn74qp7ij"
|
||||||
|
path="res://.godot/imported/Script.svg-34c66aae9c985e3e0470426acbbcda04.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/gut/images/Script.svg"
|
||||||
|
dest_files=["res://.godot/imported/Script.svg-34c66aae9c985e3e0470426acbbcda04.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
|
@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/green.png-e3a17091688e10a7013279b38edc7f8a.ct
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
compress/mode=0
|
compress/mode=0
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
compress/lossy_quality=0.7
|
||||||
compress/hdr_compression=1
|
compress/hdr_compression=1
|
||||||
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
compress/channel_pack=0
|
compress/channel_pack=0
|
||||||
mipmaps/generate=false
|
mipmaps/generate=false
|
||||||
|
|
|
@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/red.png-47a557c3922e800f76686bc1a4ad0c3c.ctex
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
compress/mode=0
|
compress/mode=0
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
compress/lossy_quality=0.7
|
||||||
compress/hdr_compression=1
|
compress/hdr_compression=1
|
||||||
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
compress/channel_pack=0
|
compress/channel_pack=0
|
||||||
mipmaps/generate=false
|
mipmaps/generate=false
|
||||||
|
|
|
@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/yellow.png-b3cf3d463958a169d909273d3d742052.c
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
compress/mode=0
|
compress/mode=0
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
compress/lossy_quality=0.7
|
||||||
compress/hdr_compression=1
|
compress/hdr_compression=1
|
||||||
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
compress/channel_pack=0
|
compress/channel_pack=0
|
||||||
mipmaps/generate=false
|
mipmaps/generate=false
|
||||||
|
|
|
@ -51,6 +51,8 @@
|
||||||
# InputEventScreenDrag
|
# InputEventScreenDrag
|
||||||
# InputEventScreenTouch
|
# InputEventScreenTouch
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
class InputQueueItem:
|
class InputQueueItem:
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
|
@ -93,6 +95,78 @@ class InputQueueItem:
|
||||||
t.connect("timeout", Callable(self, "_on_time_timeout"))
|
t.connect("timeout", Callable(self, "_on_time_timeout"))
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class MouseDraw:
|
||||||
|
extends Node2D
|
||||||
|
|
||||||
|
var down_color = Color(1, 1, 1, .25)
|
||||||
|
var up_color = Color(0, 0, 0, .25)
|
||||||
|
var line_color = Color(1, 0, 0)
|
||||||
|
var disabled = true:
|
||||||
|
get:
|
||||||
|
return disabled
|
||||||
|
set(val):
|
||||||
|
disabled = val
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
var _draw_at = Vector2(0, 0)
|
||||||
|
var _b1_down = false
|
||||||
|
var _b2_down = false
|
||||||
|
|
||||||
|
func draw_event(event):
|
||||||
|
if event is InputEventMouse:
|
||||||
|
_draw_at = event.position
|
||||||
|
if event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
|
_b1_down = event.pressed
|
||||||
|
elif event.button_index == MOUSE_BUTTON_RIGHT:
|
||||||
|
_b2_down = event.pressed
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
func _draw_cicled_cursor():
|
||||||
|
var r = 10
|
||||||
|
var b1_color = up_color
|
||||||
|
var b2_color = up_color
|
||||||
|
|
||||||
|
if _b1_down:
|
||||||
|
var pos = _draw_at - (Vector2(r * 1.5, 0))
|
||||||
|
draw_arc(pos, r / 2, 0, 360, 180, b1_color)
|
||||||
|
|
||||||
|
if _b2_down:
|
||||||
|
var pos = _draw_at + (Vector2(r * 1.5, 0))
|
||||||
|
draw_arc(pos, r / 2, 0, 360, 180, b2_color)
|
||||||
|
|
||||||
|
draw_arc(_draw_at, r, 0, 360, 360, line_color, 1)
|
||||||
|
draw_line(_draw_at - Vector2(0, r), _draw_at + Vector2(0, r), line_color)
|
||||||
|
draw_line(_draw_at - Vector2(r, 0), _draw_at + Vector2(r, 0), line_color)
|
||||||
|
|
||||||
|
func _draw_square_cursor():
|
||||||
|
var r = 10
|
||||||
|
var b1_color = up_color
|
||||||
|
var b2_color = up_color
|
||||||
|
|
||||||
|
if _b1_down:
|
||||||
|
b1_color = down_color
|
||||||
|
|
||||||
|
if _b2_down:
|
||||||
|
b2_color = down_color
|
||||||
|
|
||||||
|
var blen = r * .75
|
||||||
|
# left button rectangle
|
||||||
|
draw_rect(Rect2(_draw_at - Vector2(blen, blen), Vector2(blen, blen * 2)), b1_color)
|
||||||
|
# right button rectrangle
|
||||||
|
draw_rect(Rect2(_draw_at - Vector2(0, blen), Vector2(blen, blen * 2)), b2_color)
|
||||||
|
# Crosshair
|
||||||
|
draw_line(_draw_at - Vector2(0, r), _draw_at + Vector2(0, r), line_color)
|
||||||
|
draw_line(_draw_at - Vector2(r, 0), _draw_at + Vector2(r, 0), line_color)
|
||||||
|
|
||||||
|
func _draw():
|
||||||
|
if disabled:
|
||||||
|
return
|
||||||
|
_draw_square_cursor()
|
||||||
|
|
||||||
|
|
||||||
# ##############################################################################
|
# ##############################################################################
|
||||||
#
|
#
|
||||||
# ##############################################################################
|
# ##############################################################################
|
||||||
|
@ -105,12 +179,9 @@ var _lgr = _utils.get_logger()
|
||||||
var _receivers = []
|
var _receivers = []
|
||||||
var _input_queue = []
|
var _input_queue = []
|
||||||
var _next_queue_item = null
|
var _next_queue_item = null
|
||||||
# used by mouse_relative_motion. These use this instead of _last_event since
|
|
||||||
# it is logical to have a series of events happen between mouse motions.
|
|
||||||
var _last_mouse_motion = null
|
|
||||||
# used by hold_for and echo.
|
# used by hold_for and echo.
|
||||||
var _last_event = null
|
var _last_event = null
|
||||||
|
|
||||||
# indexed by keycode, each entry contains a boolean value indicating the
|
# indexed by keycode, each entry contains a boolean value indicating the
|
||||||
# last emitted "pressed" value for that keycode.
|
# last emitted "pressed" value for that keycode.
|
||||||
var _pressed_keys = {}
|
var _pressed_keys = {}
|
||||||
|
@ -118,6 +189,15 @@ var _pressed_actions = {}
|
||||||
var _pressed_mouse_buttons = {}
|
var _pressed_mouse_buttons = {}
|
||||||
|
|
||||||
var _auto_flush_input = false
|
var _auto_flush_input = false
|
||||||
|
var _tree_items_parent = null
|
||||||
|
var _mouse_draw = null
|
||||||
|
|
||||||
|
var _default_mouse_position = {position = Vector2(0, 0), global_position = Vector2(0, 0)}
|
||||||
|
|
||||||
|
var _last_mouse_position = {}
|
||||||
|
|
||||||
|
var mouse_warp = false
|
||||||
|
var draw_mouse = true
|
||||||
|
|
||||||
signal idle
|
signal idle
|
||||||
|
|
||||||
|
@ -126,8 +206,31 @@ func _init(r = null):
|
||||||
if r != null:
|
if r != null:
|
||||||
add_receiver(r)
|
add_receiver(r)
|
||||||
|
|
||||||
|
_last_mouse_position = _default_mouse_position.duplicate()
|
||||||
|
_tree_items_parent = Node.new()
|
||||||
|
Engine.get_main_loop().root.add_child(_tree_items_parent)
|
||||||
|
|
||||||
func _send_event(event):
|
_mouse_draw = MouseDraw.new()
|
||||||
|
_tree_items_parent.add_child(_mouse_draw)
|
||||||
|
_mouse_draw.disabled = false
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(what):
|
||||||
|
if what == NOTIFICATION_PREDELETE:
|
||||||
|
if is_instance_valid(_tree_items_parent):
|
||||||
|
_tree_items_parent.queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
func _add_queue_item(item):
|
||||||
|
item.connect("event_ready", _on_queue_item_ready.bind(item))
|
||||||
|
_next_queue_item = item
|
||||||
|
_input_queue.append(item)
|
||||||
|
_tree_items_parent.add_child(item)
|
||||||
|
if _input_queue.size() == 1:
|
||||||
|
item.start()
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_pressed_keys(event):
|
||||||
if event is InputEventKey:
|
if event is InputEventKey:
|
||||||
if (event.pressed and !event.echo) and is_key_pressed(event.keycode):
|
if (event.pressed and !event.echo) and is_key_pressed(event.keycode):
|
||||||
_lgr.warn(
|
_lgr.warn(
|
||||||
|
@ -162,6 +265,19 @@ func _send_event(event):
|
||||||
)
|
)
|
||||||
_pressed_mouse_buttons[event.button_index] = event
|
_pressed_mouse_buttons[event.button_index] = event
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_mouse_position(event):
|
||||||
|
if event is InputEventMouse:
|
||||||
|
_mouse_draw.disabled = !draw_mouse
|
||||||
|
_mouse_draw.draw_event(event)
|
||||||
|
if mouse_warp:
|
||||||
|
DisplayServer.warp_mouse(event.position)
|
||||||
|
|
||||||
|
|
||||||
|
func _send_event(event):
|
||||||
|
_handle_mouse_position(event)
|
||||||
|
_handle_pressed_keys(event)
|
||||||
|
|
||||||
for r in _receivers:
|
for r in _receivers:
|
||||||
if r == Input:
|
if r == Input:
|
||||||
Input.parse_input_event(event)
|
Input.parse_input_event(event)
|
||||||
|
@ -186,6 +302,32 @@ func _send_or_record_event(event):
|
||||||
_send_event(event)
|
_send_event(event)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_last_mouse_positions(event: InputEventMouse):
|
||||||
|
_last_mouse_position.position = event.position
|
||||||
|
_last_mouse_position.global_position = event.global_position
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_last_position_and_set_last_position(event, position, global_position):
|
||||||
|
event.position = GutUtils.nvl(position, _last_mouse_position.position)
|
||||||
|
event.global_position = GutUtils.nvl(global_position, _last_mouse_position.global_position)
|
||||||
|
_set_last_mouse_positions(event)
|
||||||
|
|
||||||
|
|
||||||
|
func _new_defaulted_mouse_button_event(position, global_position):
|
||||||
|
var event = InputEventMouseButton.new()
|
||||||
|
_apply_last_position_and_set_last_position(event, position, global_position)
|
||||||
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
func _new_defaulted_mouse_motion_event(position, global_position):
|
||||||
|
var event = InputEventMouseMotion.new()
|
||||||
|
_apply_last_position_and_set_last_position(event, position, global_position)
|
||||||
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# Events
|
||||||
|
# ------------------------------
|
||||||
func _on_queue_item_ready(item):
|
func _on_queue_item_ready(item):
|
||||||
for event in item.events:
|
for event in item.events:
|
||||||
_send_event(event)
|
_send_event(event)
|
||||||
|
@ -200,15 +342,9 @@ func _on_queue_item_ready(item):
|
||||||
_input_queue[0].start()
|
_input_queue[0].start()
|
||||||
|
|
||||||
|
|
||||||
func _add_queue_item(item):
|
# ------------------------------
|
||||||
item.connect("event_ready", _on_queue_item_ready.bind(item))
|
# Public
|
||||||
_next_queue_item = item
|
# ------------------------------
|
||||||
_input_queue.append(item)
|
|
||||||
Engine.get_main_loop().root.add_child(item)
|
|
||||||
if _input_queue.size() == 1:
|
|
||||||
item.start()
|
|
||||||
|
|
||||||
|
|
||||||
func add_receiver(obj):
|
func add_receiver(obj):
|
||||||
_receivers.append(obj)
|
_receivers.append(obj)
|
||||||
|
|
||||||
|
@ -217,6 +353,31 @@ func get_receivers():
|
||||||
return _receivers
|
return _receivers
|
||||||
|
|
||||||
|
|
||||||
|
func is_idle():
|
||||||
|
return _input_queue.size() == 0
|
||||||
|
|
||||||
|
|
||||||
|
func is_key_pressed(which):
|
||||||
|
var event = InputFactory.key_up(which)
|
||||||
|
return _pressed_keys.has(event.keycode) and _pressed_keys[event.keycode]
|
||||||
|
|
||||||
|
|
||||||
|
func is_action_pressed(which):
|
||||||
|
return _pressed_actions.has(which) and _pressed_actions[which]
|
||||||
|
|
||||||
|
|
||||||
|
func is_mouse_button_pressed(which):
|
||||||
|
return _pressed_mouse_buttons.has(which) and _pressed_mouse_buttons[which].pressed
|
||||||
|
|
||||||
|
|
||||||
|
func get_auto_flush_input():
|
||||||
|
return _auto_flush_input
|
||||||
|
|
||||||
|
|
||||||
|
func set_auto_flush_input(val):
|
||||||
|
_auto_flush_input = val
|
||||||
|
|
||||||
|
|
||||||
func wait(t):
|
func wait(t):
|
||||||
if typeof(t) == TYPE_STRING:
|
if typeof(t) == TYPE_STRING:
|
||||||
var suffix = t.substr(t.length() - 1, 1)
|
var suffix = t.substr(t.length() - 1, 1)
|
||||||
|
@ -232,16 +393,18 @@ func wait(t):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func wait_frames(num_frames):
|
func clear():
|
||||||
var item = InputQueueItem.new(0, num_frames)
|
_last_event = null
|
||||||
_add_queue_item(item)
|
_next_queue_item = null
|
||||||
return self
|
|
||||||
|
|
||||||
|
for item in _input_queue:
|
||||||
|
item.free()
|
||||||
|
_input_queue.clear()
|
||||||
|
|
||||||
func wait_secs(num_secs):
|
_pressed_keys.clear()
|
||||||
var item = InputQueueItem.new(num_secs, 0)
|
_pressed_actions.clear()
|
||||||
_add_queue_item(item)
|
_pressed_mouse_buttons.clear()
|
||||||
return self
|
_last_mouse_position = _default_mouse_position.duplicate()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
|
@ -279,53 +442,69 @@ func action_down(which, strength = 1.0):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_left_button_down(position, global_position = null):
|
func mouse_left_button_down(position = null, global_position = null):
|
||||||
var event = InputFactory.mouse_left_button_down(position, global_position)
|
var event = _new_defaulted_mouse_button_event(position, global_position)
|
||||||
|
event.pressed = true
|
||||||
|
event.button_index = MOUSE_BUTTON_LEFT
|
||||||
_send_or_record_event(event)
|
_send_or_record_event(event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_left_button_up(position, global_position = null):
|
func mouse_left_button_up(position = null, global_position = null):
|
||||||
var event = InputFactory.mouse_left_button_up(position, global_position)
|
var event = _new_defaulted_mouse_button_event(position, global_position)
|
||||||
|
event.pressed = false
|
||||||
|
event.button_index = MOUSE_BUTTON_LEFT
|
||||||
_send_or_record_event(event)
|
_send_or_record_event(event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_double_click(position, global_position = null):
|
func mouse_double_click(position = null, global_position = null):
|
||||||
var event = InputFactory.mouse_double_click(position, global_position)
|
var event = InputFactory.mouse_double_click(position, global_position)
|
||||||
event.double_click = true
|
event.double_click = true
|
||||||
_send_or_record_event(event)
|
_send_or_record_event(event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_right_button_down(position, global_position = null):
|
func mouse_right_button_down(position = null, global_position = null):
|
||||||
var event = InputFactory.mouse_right_button_down(position, global_position)
|
var event = _new_defaulted_mouse_button_event(position, global_position)
|
||||||
|
event.pressed = true
|
||||||
|
event.button_index = MOUSE_BUTTON_RIGHT
|
||||||
_send_or_record_event(event)
|
_send_or_record_event(event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_right_button_up(position, global_position = null):
|
func mouse_right_button_up(position = null, global_position = null):
|
||||||
var event = InputFactory.mouse_right_button_up(position, global_position)
|
var event = _new_defaulted_mouse_button_event(position, global_position)
|
||||||
|
event.pressed = false
|
||||||
|
event.button_index = MOUSE_BUTTON_RIGHT
|
||||||
_send_or_record_event(event)
|
_send_or_record_event(event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_motion(position, global_position = null):
|
func mouse_motion(position, global_position = null):
|
||||||
var event = InputFactory.mouse_motion(position, global_position)
|
var event = _new_defaulted_mouse_motion_event(position, global_position)
|
||||||
_last_mouse_motion = event
|
|
||||||
_send_or_record_event(event)
|
_send_or_record_event(event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_relative_motion(offset, speed = Vector2(0, 0)):
|
func mouse_relative_motion(offset, speed = Vector2(0, 0)):
|
||||||
var event = InputFactory.mouse_relative_motion(offset, _last_mouse_motion, speed)
|
var last_event = _new_defaulted_mouse_motion_event(null, null)
|
||||||
_last_mouse_motion = event
|
var event = InputFactory.mouse_relative_motion(offset, last_event, speed)
|
||||||
|
_set_last_mouse_positions(event)
|
||||||
_send_or_record_event(event)
|
_send_or_record_event(event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func mouse_set_position(position, global_position = null):
|
func mouse_set_position(position, global_position = null):
|
||||||
_last_mouse_motion = InputFactory.mouse_motion(position, global_position)
|
var event = _new_defaulted_mouse_motion_event(position, global_position)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func mouse_left_click_at(where, duration = "5f"):
|
||||||
|
wait_frames(1)
|
||||||
|
mouse_left_button_down(where)
|
||||||
|
hold_for(duration)
|
||||||
|
wait_frames(10)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,52 +531,26 @@ func release_all():
|
||||||
_send_event(event)
|
_send_event(event)
|
||||||
_pressed_mouse_buttons.clear()
|
_pressed_mouse_buttons.clear()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func wait_frames(num_frames):
|
||||||
|
var item = InputQueueItem.new(0, num_frames)
|
||||||
|
_add_queue_item(item)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func wait_secs(num_secs):
|
||||||
|
var item = InputQueueItem.new(num_secs, 0)
|
||||||
|
_add_queue_item(item)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
func hold_for(duration):
|
func hold_for(duration):
|
||||||
if _last_event != null and _last_event.pressed:
|
if _last_event != null and _last_event.pressed:
|
||||||
var next_event = _last_event.duplicate()
|
var next_event = _last_event.duplicate()
|
||||||
next_event.pressed = false
|
next_event.pressed = false
|
||||||
|
|
||||||
wait(duration)
|
wait(duration)
|
||||||
send_event(next_event)
|
send_event(next_event)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func clear():
|
|
||||||
pass
|
|
||||||
|
|
||||||
_last_event = null
|
|
||||||
_last_mouse_motion = null
|
|
||||||
_next_queue_item = null
|
|
||||||
|
|
||||||
for item in _input_queue:
|
|
||||||
item.free()
|
|
||||||
_input_queue.clear()
|
|
||||||
|
|
||||||
_pressed_keys.clear()
|
|
||||||
_pressed_actions.clear()
|
|
||||||
_pressed_mouse_buttons.clear()
|
|
||||||
|
|
||||||
|
|
||||||
func is_idle():
|
|
||||||
return _input_queue.size() == 0
|
|
||||||
|
|
||||||
|
|
||||||
func is_key_pressed(which):
|
|
||||||
var event = InputFactory.key_up(which)
|
|
||||||
return _pressed_keys.has(event.keycode) and _pressed_keys[event.keycode]
|
|
||||||
|
|
||||||
|
|
||||||
func is_action_pressed(which):
|
|
||||||
return _pressed_actions.has(which) and _pressed_actions[which]
|
|
||||||
|
|
||||||
|
|
||||||
func is_mouse_button_pressed(which):
|
|
||||||
return _pressed_mouse_buttons.has(which) and _pressed_mouse_buttons[which]
|
|
||||||
|
|
||||||
|
|
||||||
func get_auto_flush_input():
|
|
||||||
return _auto_flush_input
|
|
||||||
|
|
||||||
|
|
||||||
func set_auto_flush_input(val):
|
|
||||||
_auto_flush_input = val
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ var types = {
|
||||||
orphan = "orphan",
|
orphan = "orphan",
|
||||||
passed = "passed",
|
passed = "passed",
|
||||||
pending = "pending",
|
pending = "pending",
|
||||||
|
risky = "risky",
|
||||||
warn = "warn",
|
warn = "warn",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ var _type_data = {
|
||||||
types.orphan: {disp = "Orphans", enabled = true, fmt = fmts.yellow},
|
types.orphan: {disp = "Orphans", enabled = true, fmt = fmts.yellow},
|
||||||
types.passed: {disp = "Passed", enabled = true, fmt = fmts.green},
|
types.passed: {disp = "Passed", enabled = true, fmt = fmts.green},
|
||||||
types.pending: {disp = "Pending", enabled = true, fmt = fmts.yellow},
|
types.pending: {disp = "Pending", enabled = true, fmt = fmts.yellow},
|
||||||
|
types.risky: {disp = "Risky", enabled = true, fmt = fmts.yellow},
|
||||||
types.warn: {disp = "WARNING", enabled = true, fmt = fmts.yellow},
|
types.warn: {disp = "WARNING", enabled = true, fmt = fmts.yellow},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +79,7 @@ var _printers = {terminal = null, gui = null, console = null}
|
||||||
var _gut = null
|
var _gut = null
|
||||||
var _utils = null
|
var _utils = null
|
||||||
var _indent_level = 0
|
var _indent_level = 0
|
||||||
|
var _min_indent_level = 0
|
||||||
var _indent_string = " "
|
var _indent_string = " "
|
||||||
var _skip_test_name_for_testing = false
|
var _skip_test_name_for_testing = false
|
||||||
var _less_test_names = false
|
var _less_test_names = false
|
||||||
|
@ -130,7 +133,12 @@ func _print_test_name():
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if !cur_test.has_printed_name:
|
if !cur_test.has_printed_name:
|
||||||
_output("* " + cur_test.name + "\n")
|
var param_text = ""
|
||||||
|
if cur_test.arg_count > 0:
|
||||||
|
# Just an FYI, parameter_handler in gut might not be set yet so can't
|
||||||
|
# use it here for cooler output.
|
||||||
|
param_text = "<parameterized>"
|
||||||
|
_output(str("* ", cur_test.name, param_text, "\n"))
|
||||||
cur_test.has_printed_name = true
|
cur_test.has_printed_name = true
|
||||||
|
|
||||||
|
|
||||||
|
@ -223,6 +231,8 @@ func deprecated(text, alt_method = null):
|
||||||
|
|
||||||
func error(text):
|
func error(text):
|
||||||
_output_type(types.error, text)
|
_output_type(types.error, text)
|
||||||
|
if _gut != null:
|
||||||
|
_gut._fail_for_error(text)
|
||||||
|
|
||||||
|
|
||||||
func failed(text):
|
func failed(text):
|
||||||
|
@ -245,6 +255,10 @@ func pending(text):
|
||||||
_output_type(types.pending, text)
|
_output_type(types.pending, text)
|
||||||
|
|
||||||
|
|
||||||
|
func risky(text):
|
||||||
|
_output_type(types.risky, text)
|
||||||
|
|
||||||
|
|
||||||
func warn(text):
|
func warn(text):
|
||||||
_output_type(types.warn, text)
|
_output_type(types.warn, text)
|
||||||
|
|
||||||
|
@ -292,7 +306,7 @@ func get_indent_level():
|
||||||
|
|
||||||
|
|
||||||
func set_indent_level(indent_level):
|
func set_indent_level(indent_level):
|
||||||
_indent_level = indent_level
|
_indent_level = max(_min_indent_level, indent_level)
|
||||||
|
|
||||||
|
|
||||||
func get_indent_string():
|
func get_indent_string():
|
||||||
|
@ -313,7 +327,7 @@ func inc_indent():
|
||||||
|
|
||||||
|
|
||||||
func dec_indent():
|
func dec_indent():
|
||||||
_indent_level = max(0, _indent_level - 1)
|
_indent_level = max(_min_indent_level, _indent_level - 1)
|
||||||
|
|
||||||
|
|
||||||
func is_type_enabled(type):
|
func is_type_enabled(type):
|
||||||
|
|
|
@ -28,6 +28,7 @@ class CallParameters:
|
||||||
|
|
||||||
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
||||||
var _lgr = _utils.get_logger()
|
var _lgr = _utils.get_logger()
|
||||||
|
var default_vararg_arg_count = 10
|
||||||
const PARAM_PREFIX = "p_"
|
const PARAM_PREFIX = "p_"
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
@ -134,11 +135,15 @@ func _make_arg_array(method_meta, override_size):
|
||||||
var dflt_text = _make_stub_default(method_meta.name, i)
|
var dflt_text = _make_stub_default(method_meta.name, i)
|
||||||
to_return.append(CallParameters.new(PARAM_PREFIX + pname, dflt_text))
|
to_return.append(CallParameters.new(PARAM_PREFIX + pname, dflt_text))
|
||||||
|
|
||||||
|
var extra_params = GutUtils.nvl(override_size, 0)
|
||||||
|
if extra_params == 0:
|
||||||
|
if method_meta.flags & METHOD_FLAG_VARARG:
|
||||||
|
extra_params = default_vararg_arg_count
|
||||||
|
|
||||||
# Add in extra parameters from stub settings.
|
# Add in extra parameters from stub settings.
|
||||||
if override_size != null:
|
if extra_params > 0:
|
||||||
for i in range(method_meta.args.size(), override_size):
|
for i in range(method_meta.args.size(), extra_params):
|
||||||
var pname = str(PARAM_PREFIX, "arg", i)
|
var pname = str(PARAM_PREFIX, "arg", i)
|
||||||
print("-------- ", i, " ", pname)
|
|
||||||
var dflt_text = _make_stub_default(method_meta.name, i)
|
var dflt_text = _make_stub_default(method_meta.name, i)
|
||||||
to_return.append(CallParameters.new(pname, dflt_text))
|
to_return.append(CallParameters.new(pname, dflt_text))
|
||||||
|
|
||||||
|
@ -163,14 +168,14 @@ func _get_arg_text(arg_array):
|
||||||
|
|
||||||
|
|
||||||
# creates a call to the function in meta in the super's class.
|
# creates a call to the function in meta in the super's class.
|
||||||
func _get_super_call_text(method_name, args, super_name = ""):
|
func _get_super_call_text(method_name, args):
|
||||||
var params = ""
|
var params = ""
|
||||||
for i in range(args.size()):
|
for i in range(args.size()):
|
||||||
params += args[i].p_name
|
params += args[i].p_name
|
||||||
if i != args.size() - 1:
|
if i != args.size() - 1:
|
||||||
params += ", "
|
params += ", "
|
||||||
|
|
||||||
return str(super_name, "await super(", params, ")")
|
return str("await super(", params, ")")
|
||||||
|
|
||||||
|
|
||||||
func _get_spy_call_parameters_text(args):
|
func _get_spy_call_parameters_text(args):
|
||||||
|
@ -203,14 +208,17 @@ func _get_init_text(meta, args, method_params, param_array):
|
||||||
if i != args.size() - 1:
|
if i != args.size() - 1:
|
||||||
super_params += ", "
|
super_params += ", "
|
||||||
|
|
||||||
text = _init_text.format(
|
text = (
|
||||||
|
_init_text
|
||||||
|
. format(
|
||||||
{
|
{
|
||||||
"func_decleration": decleration,
|
"func_decleration": decleration,
|
||||||
"super_params": super_params,
|
"super_params": super_params,
|
||||||
"param_array": param_array,
|
"param_array": param_array,
|
||||||
"method_name": meta.name
|
"method_name": meta.name,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -219,16 +227,13 @@ func _get_init_text(meta, args, method_params, param_array):
|
||||||
# types whose defaults are supported will have their values. If a datatype
|
# 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
|
# is not supported and the parameter has a default, a warning message will be
|
||||||
# printed and the declaration will return null.
|
# printed and the declaration will return null.
|
||||||
#
|
func get_function_text(meta, override_size = null):
|
||||||
# path is no longer used
|
|
||||||
func get_function_text(meta, path = null, override_size = null, super_name = ""):
|
|
||||||
var method_params = ""
|
var method_params = ""
|
||||||
var text = null
|
var text = null
|
||||||
if override_size != null:
|
|
||||||
print("!!!!!! ", override_size)
|
|
||||||
var result = _make_arg_array(meta, override_size)
|
var result = _make_arg_array(meta, override_size)
|
||||||
var has_unsupported = result[0]
|
var has_unsupported = result[0]
|
||||||
var args = result[1]
|
var args = result[1]
|
||||||
|
var vararg_warning = ""
|
||||||
|
|
||||||
var param_array = _get_spy_call_parameters_text(args)
|
var param_array = _get_spy_call_parameters_text(args)
|
||||||
if has_unsupported:
|
if has_unsupported:
|
||||||
|
@ -242,20 +247,27 @@ func get_function_text(meta, path = null, override_size = null, super_name = "")
|
||||||
if param_array == "null":
|
if param_array == "null":
|
||||||
param_array = "[]"
|
param_array = "[]"
|
||||||
|
|
||||||
|
if meta.flags & METHOD_FLAG_VARARG and override_size == null:
|
||||||
|
vararg_warning = "__gutdbl.vararg_warning()\n\t"
|
||||||
|
|
||||||
if method_params != null:
|
if method_params != null:
|
||||||
if meta.name == "_init":
|
if meta.name == "_init":
|
||||||
text = _get_init_text(meta, args, method_params, param_array)
|
text = _get_init_text(meta, args, method_params, param_array)
|
||||||
else:
|
else:
|
||||||
var decleration = str("func ", meta.name, "(", method_params, "):")
|
var decleration = str("func ", meta.name, "(", method_params, "):")
|
||||||
# decleration = str('# ', meta, "\n", decleration)
|
# decleration = str('# ', meta, "\n", decleration)
|
||||||
text = _func_text.format(
|
text = (
|
||||||
|
_func_text
|
||||||
|
. format(
|
||||||
{
|
{
|
||||||
"func_decleration": decleration,
|
"func_decleration": decleration,
|
||||||
"method_name": meta.name,
|
"method_name": meta.name,
|
||||||
"param_array": param_array,
|
"param_array": param_array,
|
||||||
"super_call": _get_super_call_text(meta.name, args, super_name)
|
"super_call": _get_super_call_text(meta.name, args),
|
||||||
|
"vararg_warning": vararg_warning,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
name="Gut"
|
name="Gut"
|
||||||
description="Unit Testing tool for Godot."
|
description="Unit Testing tool for Godot."
|
||||||
author="Butch Wesley"
|
author="Butch Wesley"
|
||||||
version="7.4.1"
|
version="9.1.1"
|
||||||
script="gut_plugin.gd"
|
script="gut_plugin.gd"
|
||||||
|
|
|
@ -8,16 +8,17 @@ var _utils = load("res://addons/gut/utils.gd").get_instance()
|
||||||
var json = JSON.new()
|
var json = JSON.new()
|
||||||
|
|
||||||
|
|
||||||
func _export_tests(summary_script):
|
func _export_tests(collected_script):
|
||||||
var to_return = {}
|
var to_return = {}
|
||||||
var tests = summary_script.get_tests()
|
var tests = collected_script.tests
|
||||||
for key in tests.keys():
|
for test in tests:
|
||||||
to_return[key] = {
|
if test.get_status_text() != GutUtils.TEST_STATUSES.NOT_RUN:
|
||||||
"status": tests[key].get_status(),
|
to_return[test.name] = {
|
||||||
"passing": tests[key].pass_texts,
|
"status": test.get_status_text(),
|
||||||
"failing": tests[key].fail_texts,
|
"passing": test.pass_texts,
|
||||||
"pending": tests[key].pending_texts,
|
"failing": test.fail_texts,
|
||||||
"orphans": tests[key].orphans
|
"pending": test.pending_texts,
|
||||||
|
"orphans": test.orphans
|
||||||
}
|
}
|
||||||
|
|
||||||
return to_return
|
return to_return
|
||||||
|
@ -25,21 +26,22 @@ func _export_tests(summary_script):
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# errors
|
# errors
|
||||||
func _export_scripts(summary):
|
func _export_scripts(collector):
|
||||||
if summary == null:
|
if collector == null:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
var scripts = {}
|
var scripts = {}
|
||||||
|
|
||||||
for s in summary.get_scripts():
|
for s in collector.scripts:
|
||||||
scripts[s.name] = {
|
var test_data = _export_tests(s)
|
||||||
|
scripts[s.get_full_name()] = {
|
||||||
"props":
|
"props":
|
||||||
{
|
{
|
||||||
"tests": s._tests.size(),
|
"tests": test_data.keys().size(),
|
||||||
"pending": s.get_pending_count(),
|
"pending": s.get_pending_count(),
|
||||||
"failures": s.get_fail_count(),
|
"failures": s.get_fail_count(),
|
||||||
},
|
},
|
||||||
"tests": _export_tests(s)
|
"tests": test_data
|
||||||
}
|
}
|
||||||
return scripts
|
return scripts
|
||||||
|
|
||||||
|
@ -69,15 +71,14 @@ func _make_results_dict():
|
||||||
# time
|
# time
|
||||||
# errors
|
# errors
|
||||||
func get_results_dictionary(gut, include_scripts = true):
|
func get_results_dictionary(gut, include_scripts = true):
|
||||||
var summary = gut.get_summary()
|
|
||||||
var scripts = []
|
var scripts = []
|
||||||
|
|
||||||
if include_scripts:
|
if include_scripts:
|
||||||
scripts = _export_scripts(summary)
|
scripts = _export_scripts(gut.get_test_collector())
|
||||||
|
|
||||||
var result = _make_results_dict()
|
var result = _make_results_dict()
|
||||||
if summary != null:
|
|
||||||
var totals = summary.get_totals()
|
var totals = gut.get_summary().get_totals()
|
||||||
|
|
||||||
var props = result.test_scripts.props
|
var props = result.test_scripts.props
|
||||||
props.pending = totals.pending
|
props.pending = totals.pending
|
||||||
|
|
|
@ -1,29 +1,8 @@
|
||||||
# ------------------------------------------------------------------------------
|
# These methods didn't have flags that would exclude them from being used
|
||||||
# List of methods that should not be overloaded when they are not defined
|
# in a double and they appear to break things if they are included.
|
||||||
# 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.
|
|
||||||
const BLACKLIST = [
|
const BLACKLIST = [
|
||||||
"_draw",
|
|
||||||
"_enter_tree",
|
|
||||||
"_exit_tree",
|
|
||||||
"_get_minimum_size", # Nonexistent function _get_minimum_size
|
|
||||||
"_get", # probably
|
|
||||||
"_input",
|
|
||||||
"_notification",
|
|
||||||
"_physics_process",
|
|
||||||
"_process",
|
|
||||||
"_set",
|
|
||||||
"_to_string", # nonexistant function super._to_string
|
|
||||||
"_unhandled_input",
|
|
||||||
"_unhandled_key_input",
|
|
||||||
"draw_mesh", # issue with one parameter, value is `Null((..), (..), (..))``
|
|
||||||
"emit_signal", # can't handle extra parameters to be sent with signal.
|
|
||||||
"get_path",
|
|
||||||
"get_script",
|
"get_script",
|
||||||
"get",
|
|
||||||
"has_method",
|
"has_method",
|
||||||
"print_orphan_nodes"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,17 +13,17 @@ const BLACKLIST = [
|
||||||
# parameter
|
# parameter
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ParsedMethod:
|
class ParsedMethod:
|
||||||
|
const NO_DEFAULT = "__no__default__"
|
||||||
|
|
||||||
var _meta = {}
|
var _meta = {}
|
||||||
var meta = _meta:
|
var meta = _meta:
|
||||||
get:
|
get:
|
||||||
return _meta
|
return _meta
|
||||||
set(val):
|
set(val):
|
||||||
return
|
return
|
||||||
|
|
||||||
var _parameters = []
|
|
||||||
var is_local = false
|
var is_local = false
|
||||||
|
|
||||||
const NO_DEFAULT = "__no__default__"
|
var _parameters = []
|
||||||
|
|
||||||
func _init(metadata):
|
func _init(metadata):
|
||||||
_meta = metadata
|
_meta = metadata
|
||||||
|
@ -52,15 +31,24 @@ class ParsedMethod:
|
||||||
for i in range(_meta.args.size()):
|
for i in range(_meta.args.size()):
|
||||||
var arg = _meta.args[i]
|
var arg = _meta.args[i]
|
||||||
# Add a "default" property to the metadata so we don't have to do
|
# Add a "default" property to the metadata so we don't have to do
|
||||||
# weird default position math again.
|
# weird default paramter position math again.
|
||||||
if i >= start_default:
|
if i >= start_default:
|
||||||
arg["default"] = _meta.default_args[start_default - i]
|
arg["default"] = _meta.default_args[start_default - i]
|
||||||
else:
|
else:
|
||||||
arg["default"] = NO_DEFAULT
|
arg["default"] = NO_DEFAULT
|
||||||
_parameters.append(arg)
|
_parameters.append(arg)
|
||||||
|
|
||||||
func is_black_listed():
|
func is_eligible_for_doubling():
|
||||||
return BLACKLIST.find(_meta.name) != -1
|
var has_bad_flag = (
|
||||||
|
_meta.flags & (METHOD_FLAG_OBJECT_CORE | METHOD_FLAG_VIRTUAL | METHOD_FLAG_STATIC)
|
||||||
|
)
|
||||||
|
return !has_bad_flag and BLACKLIST.find(_meta.name) == -1
|
||||||
|
|
||||||
|
func is_accessor():
|
||||||
|
return (
|
||||||
|
_meta.name.begins_with("@")
|
||||||
|
and (_meta.name.ends_with("_getter") or _meta.name.ends_with("_setter"))
|
||||||
|
)
|
||||||
|
|
||||||
func to_s():
|
func to_s():
|
||||||
var s = _meta.name + "("
|
var s = _meta.name + "("
|
||||||
|
@ -83,8 +71,6 @@ class ParsedMethod:
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Doesn't know if a method is local and in super, but not sure if that will
|
|
||||||
# ever matter.
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ParsedScript:
|
class ParsedScript:
|
||||||
# All methods indexed by name.
|
# All methods indexed by name.
|
||||||
|
@ -147,11 +133,6 @@ class ParsedScript:
|
||||||
|
|
||||||
_parse_methods(to_load)
|
_parse_methods(to_load)
|
||||||
|
|
||||||
func _has_flag_to_be_ignored(flags):
|
|
||||||
return false
|
|
||||||
# I think this is getting anything that has the 1 flag set...I think
|
|
||||||
return flags & (1 << 2) == 0 && flags & (1 << 4) == 0 && flags & (1 << 6) == 0
|
|
||||||
|
|
||||||
func _print_flags(meta):
|
func _print_flags(meta):
|
||||||
print(
|
print(
|
||||||
str(meta.name, ":").rpad(30),
|
str(meta.name, ":").rpad(30),
|
||||||
|
@ -179,7 +160,6 @@ class ParsedScript:
|
||||||
methods = _get_native_methods(base_type)
|
methods = _get_native_methods(base_type)
|
||||||
|
|
||||||
for m in methods:
|
for m in methods:
|
||||||
if !_has_flag_to_be_ignored(m.flags):
|
|
||||||
var parsed = ParsedMethod.new(m)
|
var parsed = ParsedMethod.new(m)
|
||||||
_methods_by_name[m.name] = parsed
|
_methods_by_name[m.name] = parsed
|
||||||
# _init must always be included so that we can initialize
|
# _init must always be included so that we can initialize
|
||||||
|
@ -224,10 +204,6 @@ class ParsedScript:
|
||||||
func get_method(name):
|
func get_method(name):
|
||||||
return _methods_by_name[name]
|
return _methods_by_name[name]
|
||||||
|
|
||||||
func is_method_blacklisted(m_name):
|
|
||||||
if _methods_by_name.has(m_name):
|
|
||||||
return _methods_by_name[m_name].is_black_listed()
|
|
||||||
|
|
||||||
func get_super_method(name):
|
func get_super_method(name):
|
||||||
var to_return = get_method(name)
|
var to_return = get_method(name)
|
||||||
if to_return.is_local:
|
if to_return.is_local:
|
||||||
|
|
|
@ -211,3 +211,33 @@ func get_signals_emitted(obj):
|
||||||
emitted.append(signal_name)
|
emitted.append(signal_name)
|
||||||
|
|
||||||
return emitted
|
return emitted
|
||||||
|
|
||||||
|
|
||||||
|
func get_signal_summary(obj):
|
||||||
|
var emitted = {}
|
||||||
|
if is_watching_object(obj):
|
||||||
|
for signal_name in _watched_signals[obj]:
|
||||||
|
if _watched_signals[obj][signal_name].size() > 0:
|
||||||
|
# maybe this could return parameters if any were sent. should
|
||||||
|
# have an empty list if no parameters were ever sent to the
|
||||||
|
# signal. Or this all just gets moved into print_signal_summary
|
||||||
|
# since this wouldn't be that useful without more data in the
|
||||||
|
# summary.
|
||||||
|
var entry = {emit_count = get_emit_count(obj, signal_name)}
|
||||||
|
emitted[signal_name] = entry
|
||||||
|
|
||||||
|
return emitted
|
||||||
|
|
||||||
|
|
||||||
|
func print_signal_summary(obj):
|
||||||
|
if !is_watching_object(obj):
|
||||||
|
var msg = str("Not watching signals for ", obj)
|
||||||
|
_utils.get_logger().warn(msg)
|
||||||
|
return
|
||||||
|
|
||||||
|
var summary = get_signal_summary(obj)
|
||||||
|
print(obj, "::Signals")
|
||||||
|
var sorted = summary.keys()
|
||||||
|
sorted.sort()
|
||||||
|
for key in sorted:
|
||||||
|
print(" - ", key, " x ", summary[key].emit_count)
|
||||||
|
|
|
@ -8,37 +8,44 @@ var types = {}
|
||||||
|
|
||||||
|
|
||||||
func _init_types_dictionary():
|
func _init_types_dictionary():
|
||||||
types[TYPE_NIL] = "TYPE_NIL"
|
|
||||||
types[TYPE_BOOL] = "Bool"
|
|
||||||
types[TYPE_INT] = "Int"
|
|
||||||
types[TYPE_FLOAT] = "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_QUATERNION] = "QUAT"
|
|
||||||
types[TYPE_AABB] = "AABB"
|
types[TYPE_AABB] = "AABB"
|
||||||
#types[12] = 'Matrix3'
|
types[TYPE_ARRAY] = "ARRAY"
|
||||||
types[TYPE_TRANSFORM3D] = "Transform3D"
|
types[TYPE_BASIS] = "BASIS"
|
||||||
types[TYPE_COLOR] = "Color"
|
types[TYPE_BOOL] = "BOOL"
|
||||||
#types[15] = 'Image'
|
types[TYPE_CALLABLE] = "CALLABLE"
|
||||||
types[TYPE_NODE_PATH] = "Node Path3D"
|
types[TYPE_COLOR] = "COLOR"
|
||||||
|
types[TYPE_DICTIONARY] = "DICTIONARY"
|
||||||
|
types[TYPE_FLOAT] = "FLOAT"
|
||||||
|
types[TYPE_INT] = "INT"
|
||||||
|
types[TYPE_MAX] = "MAX"
|
||||||
|
types[TYPE_NODE_PATH] = "NODE_PATH"
|
||||||
|
types[TYPE_OBJECT] = "OBJECT"
|
||||||
|
types[TYPE_PACKED_BYTE_ARRAY] = "PACKED_BYTE_ARRAY"
|
||||||
|
types[TYPE_PACKED_COLOR_ARRAY] = "PACKED_COLOR_ARRAY"
|
||||||
|
types[TYPE_PACKED_FLOAT32_ARRAY] = "PACKED_FLOAT32_ARRAY"
|
||||||
|
types[TYPE_PACKED_FLOAT64_ARRAY] = "PACKED_FLOAT64_ARRAY"
|
||||||
|
types[TYPE_PACKED_INT32_ARRAY] = "PACKED_INT32_ARRAY"
|
||||||
|
types[TYPE_PACKED_INT64_ARRAY] = "PACKED_INT64_ARRAY"
|
||||||
|
types[TYPE_PACKED_STRING_ARRAY] = "PACKED_STRING_ARRAY"
|
||||||
|
types[TYPE_PACKED_VECTOR2_ARRAY] = "PACKED_VECTOR2_ARRAY"
|
||||||
|
types[TYPE_PACKED_VECTOR3_ARRAY] = "PACKED_VECTOR3_ARRAY"
|
||||||
|
types[TYPE_PLANE] = "PLANE"
|
||||||
|
types[TYPE_PROJECTION] = "PROJECTION"
|
||||||
|
types[TYPE_QUATERNION] = "QUATERNION"
|
||||||
|
types[TYPE_RECT2] = "RECT2"
|
||||||
|
types[TYPE_RECT2I] = "RECT2I"
|
||||||
types[TYPE_RID] = "RID"
|
types[TYPE_RID] = "RID"
|
||||||
types[TYPE_OBJECT] = "TYPE_OBJECT"
|
types[TYPE_SIGNAL] = "SIGNAL"
|
||||||
#types[19] = 'TYPE_INPUT_EVENT'
|
types[TYPE_STRING_NAME] = "STRING_NAME"
|
||||||
types[TYPE_DICTIONARY] = "Dictionary"
|
types[TYPE_STRING] = "STRING"
|
||||||
types[TYPE_ARRAY] = "Array"
|
types[TYPE_TRANSFORM2D] = "TRANSFORM2D"
|
||||||
types[TYPE_PACKED_BYTE_ARRAY] = "TYPE_PACKED_BYTE_ARRAY"
|
types[TYPE_TRANSFORM3D] = "TRANSFORM3D"
|
||||||
types[TYPE_PACKED_INT32_ARRAY] = "TYPE_PACKED_INT32_ARRAY"
|
types[TYPE_VECTOR2] = "VECTOR2"
|
||||||
types[TYPE_PACKED_FLOAT32_ARRAY] = "TYPE_PACKED_FLOAT32_ARRAY"
|
types[TYPE_VECTOR2I] = "VECTOR2I"
|
||||||
types[TYPE_PACKED_STRING_ARRAY] = "TYPE_PACKED_STRING_ARRAY"
|
types[TYPE_VECTOR3] = "VECTOR3"
|
||||||
types[TYPE_PACKED_VECTOR2_ARRAY] = "TYPE_PACKED_VECTOR2_ARRAY"
|
types[TYPE_VECTOR3I] = "VECTOR3I"
|
||||||
types[TYPE_PACKED_VECTOR3_ARRAY] = "TYPE_PACKED_VECTOR3_ARRAY"
|
types[TYPE_VECTOR4] = "VECTOR4"
|
||||||
types[TYPE_PACKED_COLOR_ARRAY] = "TYPE_PACKED_COLOR_ARRAY"
|
types[TYPE_VECTOR4I] = "VECTOR4I"
|
||||||
types[TYPE_MAX] = "TYPE_MAX"
|
|
||||||
types[TYPE_STRING_NAME] = "TYPE_STRING_NAME"
|
|
||||||
|
|
||||||
|
|
||||||
# Types to not be formatted when using _str
|
# Types to not be formatted when using _str
|
||||||
|
|
|
@ -41,9 +41,7 @@ func _init(target = null, method = null, subpath = null):
|
||||||
_lgr.warn(str(target, " is not a valid path"))
|
_lgr.warn(str(target, " is not a valid path"))
|
||||||
|
|
||||||
if stub_target is PackedScene:
|
if stub_target is PackedScene:
|
||||||
stub_target = _utils.get_scene_script_object(stub_target)
|
stub_target = GutUtils.get_scene_script_object(stub_target)
|
||||||
elif _utils.is_native_class(stub_target):
|
|
||||||
print("Got a native class: ", stub_target)
|
|
||||||
|
|
||||||
# this is used internally to stub default parameters for everything that is
|
# this is used internally to stub default parameters for everything that is
|
||||||
# doubled...or something. Look for stub_defaults_from_meta for usage. This
|
# doubled...or something. Look for stub_defaults_from_meta for usage. This
|
||||||
|
@ -77,9 +75,6 @@ func to_do_nothing():
|
||||||
|
|
||||||
|
|
||||||
func to_call_super():
|
func to_call_super():
|
||||||
if stub_method == "_init":
|
|
||||||
_lgr.error("You cannot stub _init to call super. Super's _init is always called.")
|
|
||||||
else:
|
|
||||||
call_super = true
|
call_super = true
|
||||||
_parameter_override_only = false
|
_parameter_override_only = false
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -152,7 +152,7 @@ func get_return(obj, method, parameters = null):
|
||||||
if stub_info != null:
|
if stub_info != null:
|
||||||
return stub_info.return_val
|
return stub_info.return_val
|
||||||
else:
|
else:
|
||||||
_lgr.warn(
|
_lgr.info(
|
||||||
str(
|
str(
|
||||||
"Call to [",
|
"Call to [",
|
||||||
method,
|
method,
|
||||||
|
@ -165,9 +165,6 @@ func get_return(obj, method, parameters = null):
|
||||||
|
|
||||||
|
|
||||||
func should_call_super(obj, method, parameters = null):
|
func should_call_super(obj, method, parameters = null):
|
||||||
if _utils.non_super_methods.has(method):
|
|
||||||
return false
|
|
||||||
|
|
||||||
var stub_info = _find_stub(obj, method, parameters)
|
var stub_info = _find_stub(obj, method, parameters)
|
||||||
|
|
||||||
var is_partial = false
|
var is_partial = false
|
||||||
|
|
|
@ -1,287 +1,204 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Contains all the results of a single test. Allows for multiple asserts results
|
# Prints things, mostly. Knows too much about gut.gd, but it's only supposed to
|
||||||
# and pending calls.
|
# work with gut.gd, so I'm fine with that.
|
||||||
#
|
|
||||||
# When determining the status of a test, check for failing then passing then
|
|
||||||
# pending.
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Test:
|
# a _test_collector to use when one is not provided.
|
||||||
var pass_texts = []
|
var _gut = null
|
||||||
var fail_texts = []
|
|
||||||
var pending_texts = []
|
|
||||||
var orphans = 0
|
|
||||||
var line_number = 0
|
|
||||||
|
|
||||||
# must have passed an assert and not have any other status to be passing
|
|
||||||
func is_passing():
|
|
||||||
return pass_texts.size() > 0 and fail_texts.size() == 0 and pending_texts.size() == 0
|
|
||||||
|
|
||||||
# failing takes precedence over everything else, so any failures makes the
|
|
||||||
# test a failure.
|
|
||||||
func is_failing():
|
|
||||||
return fail_texts.size() > 0
|
|
||||||
|
|
||||||
# test is only pending if pending was called and the test is not failing.
|
|
||||||
func is_pending():
|
|
||||||
return pending_texts.size() > 0 and fail_texts.size() == 0
|
|
||||||
|
|
||||||
func did_something():
|
|
||||||
return is_passing() or is_failing() or is_pending()
|
|
||||||
|
|
||||||
# NOTE: The "failed" and "pending" text must match what is outputted by
|
|
||||||
# the logger in order for text highlighting to occur in summary.
|
|
||||||
func to_s():
|
|
||||||
var pad = " "
|
|
||||||
var to_return = ""
|
|
||||||
for i in range(fail_texts.size()):
|
|
||||||
to_return += str(pad, "[Failed]: ", fail_texts[i], "\n")
|
|
||||||
for i in range(pending_texts.size()):
|
|
||||||
to_return += str(pad, "[Pending]: ", pending_texts[i], "\n")
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
func get_status():
|
|
||||||
var to_return = "no asserts"
|
|
||||||
if pending_texts.size() > 0:
|
|
||||||
to_return = "pending"
|
|
||||||
elif fail_texts.size() > 0:
|
|
||||||
to_return = "fail"
|
|
||||||
elif pass_texts.size() > 0:
|
|
||||||
to_return = "pass"
|
|
||||||
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
func _init(gut = null):
|
||||||
# Contains all the results for a single test-script/inner class. Persists the
|
_gut = gut
|
||||||
# names of the tests and results and the order in which the tests were run.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
class TestScript:
|
|
||||||
var name = "NOT_SET"
|
|
||||||
var was_skipped = false
|
|
||||||
var skip_reason = ""
|
|
||||||
var _tests = {}
|
|
||||||
var _test_order = []
|
|
||||||
|
|
||||||
func _init(script_name):
|
|
||||||
name = script_name
|
|
||||||
|
|
||||||
func get_pass_count():
|
# ---------------------
|
||||||
var count = 0
|
# Private
|
||||||
for key in _tests:
|
# ---------------------
|
||||||
count += _tests[key].pass_texts.size()
|
func _log_end_run_header(gut):
|
||||||
return count
|
var lgr = gut.get_logger()
|
||||||
|
lgr.log("\n\n\n")
|
||||||
|
lgr.log("==============================================", lgr.fmts.yellow)
|
||||||
|
lgr.log("= Run Summary", lgr.fmts.yellow)
|
||||||
|
lgr.log("==============================================", lgr.fmts.yellow)
|
||||||
|
|
||||||
func get_fail_count():
|
|
||||||
var count = 0
|
|
||||||
for key in _tests:
|
|
||||||
count += _tests[key].fail_texts.size()
|
|
||||||
return count
|
|
||||||
|
|
||||||
func get_pending_count():
|
func _log_what_was_run(gut):
|
||||||
var count = 0
|
if !gut._utils.is_null_or_empty(gut._select_script):
|
||||||
for key in _tests:
|
gut.p('Ran Scripts matching "' + gut._select_script + '"')
|
||||||
count += _tests[key].pending_texts.size()
|
if !gut._utils.is_null_or_empty(gut._unit_test_name):
|
||||||
return count
|
gut.p('Ran Tests matching "' + gut._unit_test_name + '"')
|
||||||
|
if !gut._utils.is_null_or_empty(gut._inner_class_name):
|
||||||
|
gut.p('Ran Inner Classes matching "' + gut._inner_class_name + '"')
|
||||||
|
|
||||||
func get_passing_test_count():
|
|
||||||
var count = 0
|
|
||||||
for key in _tests:
|
|
||||||
if _tests[key].is_passing():
|
|
||||||
count += 1
|
|
||||||
return count
|
|
||||||
|
|
||||||
func get_failing_test_count():
|
func _log_orphans_and_disclaimer(gut):
|
||||||
var count = 0
|
var orphan_count = gut.get_orphan_counter()
|
||||||
for key in _tests:
|
var lgr = gut.get_logger()
|
||||||
if _tests[key].is_failing():
|
# Do not count any of the _test_script_objects since these will be released
|
||||||
count += 1
|
# when GUT is released.
|
||||||
return count
|
orphan_count._counters.total += gut._test_script_objects.size()
|
||||||
|
if orphan_count.get_counter("total") > 0 and lgr.is_type_enabled("orphan"):
|
||||||
|
orphan_count.print_orphans("total", lgr)
|
||||||
|
gut.p("Note: This count does not include GUT objects that will be freed upon exit.")
|
||||||
|
gut.p(" It also does not include any orphans created by global scripts")
|
||||||
|
gut.p(" loaded before tests were ran.")
|
||||||
|
gut.p(str("Total orphans = ", orphan_count.orphan_count()))
|
||||||
|
gut.p("")
|
||||||
|
|
||||||
func get_risky_count():
|
|
||||||
var count = 0
|
func _total_fmt(text, value):
|
||||||
if was_skipped:
|
var space = 18
|
||||||
count = 1
|
if str(value) == "0":
|
||||||
|
value = "none"
|
||||||
|
return str(text.rpad(space), value)
|
||||||
|
|
||||||
|
|
||||||
|
func _log_non_zero_total(text, value, lgr):
|
||||||
|
if str(value) != "0":
|
||||||
|
lgr.log(_total_fmt(text, value))
|
||||||
|
return 1
|
||||||
else:
|
else:
|
||||||
for key in _tests:
|
return 0
|
||||||
if !_tests[key].did_something():
|
|
||||||
count += 1
|
|
||||||
return count
|
|
||||||
|
|
||||||
func get_test_obj(obj_name):
|
|
||||||
if !_tests.has(obj_name):
|
|
||||||
var to_add = Test.new()
|
|
||||||
_tests[obj_name] = to_add
|
|
||||||
_test_order.append(obj_name)
|
|
||||||
|
|
||||||
var to_return = _tests[obj_name]
|
|
||||||
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
func add_pass(test_name, reason):
|
|
||||||
var t = get_test_obj(test_name)
|
|
||||||
t.pass_texts.append(reason)
|
|
||||||
|
|
||||||
func add_fail(test_name, reason):
|
|
||||||
var t = get_test_obj(test_name)
|
|
||||||
t.fail_texts.append(reason)
|
|
||||||
|
|
||||||
func add_pending(test_name, reason):
|
|
||||||
var t = get_test_obj(test_name)
|
|
||||||
t.pending_texts.append(reason)
|
|
||||||
|
|
||||||
func get_tests():
|
|
||||||
return _tests
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
func _log_totals(gut, totals):
|
||||||
# Summary Class
|
var lgr = gut.get_logger()
|
||||||
#
|
lgr.log()
|
||||||
# This class holds the results of all the test scripts and Inner Classes that
|
|
||||||
# were run.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
var _scripts = []
|
|
||||||
|
|
||||||
|
lgr.log("---- Totals ----")
|
||||||
|
var col1 = 18
|
||||||
|
var issue_count = 0
|
||||||
|
issue_count += _log_non_zero_total("Errors", totals.errors, lgr)
|
||||||
|
issue_count += _log_non_zero_total("Warnings", totals.warnings, lgr)
|
||||||
|
issue_count += _log_non_zero_total("Deprecated", totals.deprecated, lgr)
|
||||||
|
if issue_count > 0:
|
||||||
|
lgr.log("")
|
||||||
|
|
||||||
func add_script(name):
|
lgr.log(_total_fmt("Scripts", totals.scripts))
|
||||||
_scripts.append(TestScript.new(name))
|
lgr.log(_total_fmt("Tests", gut.get_test_collector().get_ran_test_count()))
|
||||||
|
lgr.log(_total_fmt(" Passing", totals.passing_tests))
|
||||||
|
_log_non_zero_total(" Failing", totals.failing_tests, lgr)
|
||||||
func get_scripts():
|
_log_non_zero_total(" Risky/Pending", totals.risky + totals.pending, lgr)
|
||||||
return _scripts
|
lgr.log(_total_fmt("Asserts", totals.passing + totals.failing))
|
||||||
|
lgr.log(_total_fmt("Time", str(gut.get_elapsed_time(), "s")))
|
||||||
|
|
||||||
func get_current_script():
|
|
||||||
return _scripts[_scripts.size() - 1]
|
|
||||||
|
|
||||||
|
|
||||||
func add_test(test_name):
|
|
||||||
# print('-- test_name = ', test_name)
|
|
||||||
# print('-- current script = ', get_current_script())
|
|
||||||
# print('-- test_obj = ', get_current_script().get_test_obj(test_name))
|
|
||||||
return get_current_script().get_test_obj(test_name)
|
|
||||||
|
|
||||||
|
|
||||||
func add_pass(test_name, reason = ""):
|
|
||||||
get_current_script().add_pass(test_name, reason)
|
|
||||||
|
|
||||||
|
|
||||||
func add_fail(test_name, reason = ""):
|
|
||||||
get_current_script().add_fail(test_name, reason)
|
|
||||||
|
|
||||||
|
|
||||||
func add_pending(test_name, reason = ""):
|
|
||||||
get_current_script().add_pending(test_name, reason)
|
|
||||||
|
|
||||||
|
|
||||||
func get_test_text(test_name):
|
|
||||||
return test_name + "\n" + get_current_script().get_test_obj(test_name).to_s()
|
|
||||||
|
|
||||||
|
|
||||||
# Gets the count of unique script names minus the .<Inner Class Name> at the
|
|
||||||
# end. Used for displaying the number of scripts without including all the
|
|
||||||
# Inner Classes.
|
|
||||||
func get_non_inner_class_script_count():
|
|
||||||
var counter = load("res://addons/gut/thing_counter.gd").new()
|
|
||||||
for i in range(_scripts.size()):
|
|
||||||
var ext_loc = _scripts[i].name.rfind(".gd.")
|
|
||||||
var to_add = _scripts[i].name
|
|
||||||
if ext_loc != -1:
|
|
||||||
to_add = _scripts[i].name.substr(0, ext_loc + 3)
|
|
||||||
|
|
||||||
counter.add(to_add)
|
|
||||||
return counter.get_unique_count()
|
|
||||||
|
|
||||||
|
|
||||||
func get_totals():
|
|
||||||
var totals = {
|
|
||||||
passing = 0,
|
|
||||||
pending = 0,
|
|
||||||
failing = 0,
|
|
||||||
risky = 0,
|
|
||||||
tests = 0,
|
|
||||||
scripts = 0,
|
|
||||||
passing_tests = 0,
|
|
||||||
failing_tests = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in range(_scripts.size()):
|
|
||||||
# assert totals
|
|
||||||
totals.passing += _scripts[i].get_pass_count()
|
|
||||||
totals.pending += _scripts[i].get_pending_count()
|
|
||||||
totals.failing += _scripts[i].get_fail_count()
|
|
||||||
|
|
||||||
# test totals
|
|
||||||
totals.tests += _scripts[i]._test_order.size()
|
|
||||||
totals.passing_tests += _scripts[i].get_passing_test_count()
|
|
||||||
totals.failing_tests += _scripts[i].get_failing_test_count()
|
|
||||||
totals.risky += _scripts[i].get_risky_count()
|
|
||||||
|
|
||||||
totals.scripts = get_non_inner_class_script_count()
|
|
||||||
|
|
||||||
return totals
|
return totals
|
||||||
|
|
||||||
|
|
||||||
func log_summary_text(lgr):
|
# ---------------------
|
||||||
var orig_indent = lgr.get_indent_level()
|
# Public
|
||||||
var found_failing_or_pending = false
|
# ---------------------
|
||||||
|
func log_all_non_passing_tests(gut = _gut):
|
||||||
|
var test_collector = gut.get_test_collector()
|
||||||
|
var lgr = gut.get_logger()
|
||||||
|
|
||||||
for s in range(_scripts.size()):
|
var to_return = {passing = 0, non_passing = 0}
|
||||||
|
|
||||||
|
for test_script in test_collector.scripts:
|
||||||
lgr.set_indent_level(0)
|
lgr.set_indent_level(0)
|
||||||
if (
|
|
||||||
_scripts[s].was_skipped
|
|
||||||
or _scripts[s].get_fail_count() > 0
|
|
||||||
or _scripts[s].get_pending_count() > 0
|
|
||||||
):
|
|
||||||
lgr.log(_scripts[s].name, lgr.fmts.underline)
|
|
||||||
|
|
||||||
if _scripts[s].was_skipped:
|
if (
|
||||||
|
test_script.was_skipped
|
||||||
|
or test_script.get_fail_count() > 0
|
||||||
|
or test_script.get_pending_count() > 0
|
||||||
|
):
|
||||||
|
lgr.log("\n" + test_script.get_full_name(), lgr.fmts.underline)
|
||||||
|
|
||||||
|
if test_script.was_skipped:
|
||||||
lgr.inc_indent()
|
lgr.inc_indent()
|
||||||
var skip_msg = str("[Risky] Script was skipped: ", _scripts[s].skip_reason)
|
var skip_msg = str("[Risky] Script was skipped: ", test_script.skip_reason)
|
||||||
lgr.log(skip_msg, lgr.fmts.yellow)
|
lgr.log(skip_msg, lgr.fmts.yellow)
|
||||||
lgr.dec_indent()
|
lgr.dec_indent()
|
||||||
|
|
||||||
for t in range(_scripts[s]._test_order.size()):
|
for test in test_script.tests:
|
||||||
var tname = _scripts[s]._test_order[t]
|
if test.was_run:
|
||||||
var test = _scripts[s].get_test_obj(tname)
|
if test.is_passing():
|
||||||
if !test.is_passing():
|
to_return.passing += 1
|
||||||
found_failing_or_pending = true
|
else:
|
||||||
lgr.log(str("- ", tname))
|
to_return.non_passing += 1
|
||||||
|
lgr.log(str("- ", test.name))
|
||||||
lgr.inc_indent()
|
lgr.inc_indent()
|
||||||
|
|
||||||
for i in range(test.fail_texts.size()):
|
for i in range(test.fail_texts.size()):
|
||||||
lgr.failed(test.fail_texts[i])
|
lgr.failed(test.fail_texts[i])
|
||||||
for i in range(test.pending_texts.size()):
|
for i in range(test.pending_texts.size()):
|
||||||
lgr.pending(test.pending_texts[i])
|
lgr.pending(test.pending_texts[i])
|
||||||
if !test.did_something():
|
if test.is_risky():
|
||||||
lgr.log("[Risky] Did not assert", lgr.fmts.yellow)
|
lgr.risky("Did not assert")
|
||||||
lgr.dec_indent()
|
lgr.dec_indent()
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
func log_the_final_line(totals, gut):
|
||||||
|
var lgr = gut.get_logger()
|
||||||
|
var grand_total_text = ""
|
||||||
|
var grand_total_fmt = lgr.fmts.none
|
||||||
|
if totals.failing_tests > 0:
|
||||||
|
grand_total_text = str(totals.failing_tests, " failing tests")
|
||||||
|
grand_total_fmt = lgr.fmts.red
|
||||||
|
elif totals.risky > 0 or totals.pending > 0:
|
||||||
|
grand_total_text = str(totals.risky + totals.pending, " pending/risky tests.")
|
||||||
|
grand_total_fmt = lgr.fmts.yellow
|
||||||
|
else:
|
||||||
|
grand_total_text = "All tests passed!"
|
||||||
|
grand_total_fmt = lgr.fmts.green
|
||||||
|
|
||||||
|
lgr.log(str("---- ", grand_total_text, " ----"), grand_total_fmt)
|
||||||
|
|
||||||
|
|
||||||
|
func log_totals(gut, totals):
|
||||||
|
var lgr = gut.get_logger()
|
||||||
|
var orig_indent = lgr.get_indent_level()
|
||||||
lgr.set_indent_level(0)
|
lgr.set_indent_level(0)
|
||||||
if !found_failing_or_pending:
|
_log_totals(gut, totals)
|
||||||
lgr.log("All tests passed", lgr.fmts.green)
|
|
||||||
|
|
||||||
# just picked a non-printable char, dunno if it is a good or bad choice.
|
|
||||||
var npws = PackedByteArray([31]).get_string_from_ascii()
|
|
||||||
|
|
||||||
lgr.log()
|
|
||||||
var _totals = get_totals()
|
|
||||||
lgr.log("Totals", lgr.fmts.yellow)
|
|
||||||
lgr.log(str("Scripts: ", get_non_inner_class_script_count()))
|
|
||||||
lgr.log(str("Passing tests ", _totals.passing_tests))
|
|
||||||
lgr.log(str("Failing tests ", _totals.failing_tests))
|
|
||||||
lgr.log(str("Risky tests ", _totals.risky))
|
|
||||||
var pnd = str("Pending: ", _totals.pending)
|
|
||||||
# add a non printable character so this "pending" isn't highlighted in the
|
|
||||||
# editor's output panel.
|
|
||||||
lgr.log(str(npws, pnd))
|
|
||||||
lgr.log(
|
|
||||||
str(
|
|
||||||
"Asserts: ",
|
|
||||||
_totals.passing,
|
|
||||||
" of ",
|
|
||||||
_totals.passing + _totals.failing,
|
|
||||||
" passed"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
lgr.set_indent_level(orig_indent)
|
lgr.set_indent_level(orig_indent)
|
||||||
|
|
||||||
|
|
||||||
|
func get_totals(gut = _gut):
|
||||||
|
var tc = gut.get_test_collector()
|
||||||
|
var lgr = gut.get_logger()
|
||||||
|
|
||||||
|
var totals = {
|
||||||
|
failing = 0,
|
||||||
|
failing_tests = 0,
|
||||||
|
passing = 0,
|
||||||
|
passing_tests = 0,
|
||||||
|
pending = 0,
|
||||||
|
risky = 0,
|
||||||
|
scripts = tc.get_ran_script_count(),
|
||||||
|
tests = 0,
|
||||||
|
deprecated = lgr.get_deprecated().size(),
|
||||||
|
errors = lgr.get_errors().size(),
|
||||||
|
warnings = lgr.get_warnings().size(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for s in tc.scripts:
|
||||||
|
# assert totals
|
||||||
|
totals.passing += s.get_pass_count()
|
||||||
|
totals.pending += s.get_pending_count()
|
||||||
|
totals.failing += s.get_fail_count()
|
||||||
|
|
||||||
|
# test totals
|
||||||
|
totals.tests += s.get_ran_test_count()
|
||||||
|
totals.passing_tests += s.get_passing_test_count()
|
||||||
|
totals.failing_tests += s.get_failing_test_count()
|
||||||
|
totals.risky += s.get_risky_count()
|
||||||
|
|
||||||
|
return totals
|
||||||
|
|
||||||
|
|
||||||
|
func log_end_run(gut = _gut):
|
||||||
|
_log_end_run_header(gut)
|
||||||
|
|
||||||
|
var totals = get_totals(gut)
|
||||||
|
var tc = gut.get_test_collector()
|
||||||
|
var lgr = gut.get_logger()
|
||||||
|
|
||||||
|
log_all_non_passing_tests(gut)
|
||||||
|
log_totals(gut, totals)
|
||||||
|
lgr.log("\n")
|
||||||
|
|
||||||
|
_log_orphans_and_disclaimer(gut)
|
||||||
|
_log_what_was_run(gut)
|
||||||
|
log_the_final_line(totals, gut)
|
||||||
|
lgr.log("")
|
||||||
|
|
|
@ -61,7 +61,7 @@ var _summary = {asserts = 0, passed = 0, failed = 0, tests = 0, pending = 0}
|
||||||
var _signal_watcher = load("res://addons/gut/signal_watcher.gd").new()
|
var _signal_watcher = load("res://addons/gut/signal_watcher.gd").new()
|
||||||
|
|
||||||
# Convenience copy of _utils.DOUBLE_STRATEGY
|
# Convenience copy of _utils.DOUBLE_STRATEGY
|
||||||
var DOUBLE_STRATEGY = _utils.DOUBLE_STRATEGY
|
var DOUBLE_STRATEGY = GutUtils.DOUBLE_STRATEGY
|
||||||
|
|
||||||
var _lgr = _utils.get_logger()
|
var _lgr = _utils.get_logger()
|
||||||
var _strutils = _utils.Strutils.new()
|
var _strutils = _utils.Strutils.new()
|
||||||
|
@ -89,7 +89,7 @@ func _fail(text):
|
||||||
_summary.failed += 1
|
_summary.failed += 1
|
||||||
_fail_pass_text.append("failed: " + text)
|
_fail_pass_text.append("failed: " + text)
|
||||||
if gut:
|
if gut:
|
||||||
_lgr.failed(text)
|
_lgr.failed(gut.get_call_count_text() + text)
|
||||||
gut._fail(text)
|
gut._fail(text)
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,6 +215,58 @@ func _fail_if_parameters_not_array(parameters):
|
||||||
return invalid
|
return invalid
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# A bunch of common checkes used when validating a double/method pair. If
|
||||||
|
# everything is ok then an empty string is returned, otherwise the message
|
||||||
|
# is returned.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
func _get_bad_double_or_method_message(inst, method_name, what_you_cant_do):
|
||||||
|
var to_return = ""
|
||||||
|
|
||||||
|
if !_utils.is_double(inst):
|
||||||
|
to_return = str("An instance of a Double was expected, you passed: ", _str(inst))
|
||||||
|
elif !inst.has_method(method_name):
|
||||||
|
to_return = str(
|
||||||
|
"You cannot ",
|
||||||
|
what_you_cant_do,
|
||||||
|
" [",
|
||||||
|
method_name,
|
||||||
|
"] because the method does not exist. ",
|
||||||
|
"This can happen if the method is virtual and not overloaded (i.e. _ready) ",
|
||||||
|
"or you have mistyped the name of the method."
|
||||||
|
)
|
||||||
|
elif !inst.__gutdbl_values.doubled_methods.has(method_name):
|
||||||
|
to_return = str(
|
||||||
|
"You cannot ",
|
||||||
|
what_you_cant_do,
|
||||||
|
" [",
|
||||||
|
method_name,
|
||||||
|
"] because ",
|
||||||
|
_str(inst),
|
||||||
|
" does not overload it or it was ignored with ",
|
||||||
|
"ignore_method_when_doubling. See Doubling ",
|
||||||
|
"Strategy in the wiki for details on including non-overloaded ",
|
||||||
|
"methods in a double."
|
||||||
|
)
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
func _fail_if_not_double_or_does_not_have_method(inst, method_name):
|
||||||
|
var to_return = OK
|
||||||
|
|
||||||
|
var msg = _get_bad_double_or_method_message(inst, method_name, "spy on")
|
||||||
|
if msg != "":
|
||||||
|
_fail(msg)
|
||||||
|
to_return = ERR_INVALID_DATA
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
func _create_obj_from_type(type):
|
func _create_obj_from_type(type):
|
||||||
var obj = null
|
var obj = null
|
||||||
if type.is_class("PackedScene"):
|
if type.is_class("PackedScene"):
|
||||||
|
@ -276,13 +328,16 @@ func assert_eq(got, expected, text = ""):
|
||||||
var disp = "[" + _str(got) + "] expected to equal [" + _str(expected) + "]: " + text
|
var disp = "[" + _str(got) + "] expected to equal [" + _str(expected) + "]: " + text
|
||||||
var result = null
|
var result = null
|
||||||
|
|
||||||
if typeof(got) == TYPE_ARRAY:
|
|
||||||
result = _compare.shallow(got, expected)
|
|
||||||
else:
|
|
||||||
result = _compare.simple(got, expected)
|
result = _compare.simple(got, expected)
|
||||||
|
|
||||||
if typeof(got) in [TYPE_ARRAY, TYPE_DICTIONARY]:
|
if typeof(got) in [TYPE_ARRAY, TYPE_DICTIONARY]:
|
||||||
disp = str(result.summary, " ", text)
|
disp = str(result.summary, " ", text)
|
||||||
|
(
|
||||||
|
_lgr
|
||||||
|
. info(
|
||||||
|
"Array/Dictionary compared by value. Use assert_same to compare references. Use assert_eq_deep to see diff when failing."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if result.are_equal:
|
if result.are_equal:
|
||||||
_pass(disp)
|
_pass(disp)
|
||||||
|
@ -300,13 +355,16 @@ func assert_ne(got, not_expected, text = ""):
|
||||||
)
|
)
|
||||||
var result = null
|
var result = null
|
||||||
|
|
||||||
if typeof(got) == TYPE_ARRAY:
|
|
||||||
result = _compare.shallow(got, not_expected)
|
|
||||||
else:
|
|
||||||
result = _compare.simple(got, not_expected)
|
result = _compare.simple(got, not_expected)
|
||||||
|
|
||||||
if typeof(got) in [TYPE_ARRAY, TYPE_DICTIONARY]:
|
if typeof(got) in [TYPE_ARRAY, TYPE_DICTIONARY]:
|
||||||
disp = str(result.summary, " ", text)
|
disp = str(result.summary, " ", text)
|
||||||
|
(
|
||||||
|
_lgr
|
||||||
|
. info(
|
||||||
|
"Array/Dictionary compared by value. Use assert_not_same to compare references. Use assert_ne_deep to see diff."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if result.are_equal:
|
if result.are_equal:
|
||||||
_fail(disp)
|
_fail(disp)
|
||||||
|
@ -927,7 +985,7 @@ func assert_is(object, a_class, text = ""):
|
||||||
if !_utils.is_native_class(a_class) and !_utils.is_gdscript(a_class):
|
if !_utils.is_native_class(a_class) and !_utils.is_gdscript(a_class):
|
||||||
_fail(str(bad_param_2, a_str))
|
_fail(str(bad_param_2, a_str))
|
||||||
else:
|
else:
|
||||||
if object is a_class:
|
if is_instance_of(object, a_class):
|
||||||
_pass(disp)
|
_pass(disp)
|
||||||
else:
|
else:
|
||||||
_fail(disp)
|
_fail(disp)
|
||||||
|
@ -1047,11 +1105,7 @@ func assert_called(inst, method_name, parameters = null):
|
||||||
if _fail_if_parameters_not_array(parameters):
|
if _fail_if_parameters_not_array(parameters):
|
||||||
return
|
return
|
||||||
|
|
||||||
if !_utils.is_double(inst):
|
if _fail_if_not_double_or_does_not_have_method(inst, method_name) == OK:
|
||||||
_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):
|
if gut.get_spy().was_called(inst, method_name, parameters):
|
||||||
_pass(disp)
|
_pass(disp)
|
||||||
else:
|
else:
|
||||||
|
@ -1071,11 +1125,7 @@ func assert_not_called(inst, method_name, parameters = null):
|
||||||
if _fail_if_parameters_not_array(parameters):
|
if _fail_if_parameters_not_array(parameters):
|
||||||
return
|
return
|
||||||
|
|
||||||
if !_utils.is_double(inst):
|
if _fail_if_not_double_or_does_not_have_method(inst, method_name) == OK:
|
||||||
_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 gut.get_spy().was_called(inst, method_name, parameters):
|
||||||
if parameters != null:
|
if parameters != null:
|
||||||
disp += str(" with parameters ", parameters)
|
disp += str(" with parameters ", parameters)
|
||||||
|
@ -1101,11 +1151,7 @@ func assert_call_count(inst, method_name, expected_count, parameters = null):
|
||||||
var disp = "Expected [%s] on %s to be called [%s] times%s. It was called [%s] times."
|
var disp = "Expected [%s] on %s to be called [%s] times%s. It was called [%s] times."
|
||||||
disp = disp % [method_name, _str(inst), expected_count, param_text, count]
|
disp = disp % [method_name, _str(inst), expected_count, param_text, count]
|
||||||
|
|
||||||
if !_utils.is_double(inst):
|
if _fail_if_not_double_or_does_not_have_method(inst, method_name) == OK:
|
||||||
_fail(
|
|
||||||
"You must pass a doubled instance to assert_call_count. Check the wiki for info on using double."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if count == expected_count:
|
if count == expected_count:
|
||||||
_pass(disp)
|
_pass(disp)
|
||||||
else:
|
else:
|
||||||
|
@ -1254,7 +1300,7 @@ func assert_property_with_backing_variable(
|
||||||
):
|
):
|
||||||
var setter_name = str("@", property_name, "_setter")
|
var setter_name = str("@", property_name, "_setter")
|
||||||
var getter_name = str("@", property_name, "_getter")
|
var getter_name = str("@", property_name, "_getter")
|
||||||
var backing_name = _utils.nvl(backed_by_name, str("_", property_name))
|
var backing_name = GutUtils.nvl(backed_by_name, str("_", property_name))
|
||||||
var pre_fail_count = get_fail_count()
|
var pre_fail_count = get_fail_count()
|
||||||
|
|
||||||
var props = obj.get_property_list()
|
var props = obj.get_property_list()
|
||||||
|
@ -1438,7 +1484,7 @@ func get_summary_text():
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func _smart_double(thing, double_strat, partial):
|
func _smart_double(thing, double_strat, partial):
|
||||||
var override_strat = _utils.nvl(double_strat, gut.get_doubler().get_strategy())
|
var override_strat = GutUtils.nvl(double_strat, gut.get_doubler().get_strategy())
|
||||||
var to_return = null
|
var to_return = null
|
||||||
|
|
||||||
if thing is PackedScene:
|
if thing is PackedScene:
|
||||||
|
@ -1531,7 +1577,7 @@ func double_scene(path, strategy = null):
|
||||||
_lgr.deprecated("test.double_scene has been removed.", "double")
|
_lgr.deprecated("test.double_scene has been removed.", "double")
|
||||||
return null
|
return null
|
||||||
|
|
||||||
# var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy())
|
# var override_strat = GutUtils.nvl(strategy, gut.get_doubler().get_strategy())
|
||||||
# return gut.get_doubler().double_scene(path, override_strat)
|
# return gut.get_doubler().double_scene(path, override_strat)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1542,7 +1588,7 @@ func double_script(path, strategy = null):
|
||||||
_lgr.deprecated("test.double_script has been removed.", "double")
|
_lgr.deprecated("test.double_script has been removed.", "double")
|
||||||
return null
|
return null
|
||||||
|
|
||||||
# var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy())
|
# var override_strat = GutUtils.nvl(strategy, gut.get_doubler().get_strategy())
|
||||||
# return gut.get_doubler().double(path, override_strat)
|
# return gut.get_doubler().double(path, override_strat)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1555,7 +1601,7 @@ func double_inner(path, subpath, strategy = null):
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy())
|
var override_strat = GutUtils.nvl(strategy, gut.get_doubler().get_strategy())
|
||||||
return gut.get_doubler().double_inner(path, subpath, override_strat)
|
return gut.get_doubler().double_inner(path, subpath, override_strat)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1574,11 +1620,8 @@ func ignore_method_when_doubling(thing, method_name):
|
||||||
return
|
return
|
||||||
|
|
||||||
var r = thing
|
var r = thing
|
||||||
|
|
||||||
if thing is PackedScene:
|
if thing is PackedScene:
|
||||||
var inst = thing.instantiate()
|
r = GutUtils.get_scene_script_object(thing)
|
||||||
if inst.get_script():
|
|
||||||
r = inst.get_script()
|
|
||||||
|
|
||||||
gut.get_doubler().add_ignored_method(r, method_name)
|
gut.get_doubler().add_ignored_method(r, method_name)
|
||||||
|
|
||||||
|
@ -1595,16 +1638,18 @@ func ignore_method_when_doubling(thing, method_name):
|
||||||
# to leave it but not update the wiki.
|
# to leave it but not update the wiki.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func stub(thing, p2, p3 = null):
|
func stub(thing, p2, p3 = null):
|
||||||
if _utils.is_instance(thing) and !_utils.is_double(thing):
|
|
||||||
_lgr.error(str("You cannot use stub on ", _str(thing), " because it is not a double."))
|
|
||||||
return _utils.StubParams.new()
|
|
||||||
|
|
||||||
var method_name = p2
|
var method_name = p2
|
||||||
var subpath = null
|
var subpath = null
|
||||||
if p3 != null:
|
if p3 != null:
|
||||||
subpath = p2
|
subpath = p2
|
||||||
method_name = p3
|
method_name = p3
|
||||||
|
|
||||||
|
if _utils.is_instance(thing):
|
||||||
|
var msg = _get_bad_double_or_method_message(thing, method_name, "stub")
|
||||||
|
if msg != "":
|
||||||
|
_lgr.error(msg)
|
||||||
|
return _utils.StubParams.new()
|
||||||
|
|
||||||
var sp = _utils.StubParams.new(thing, method_name, subpath)
|
var sp = _utils.StubParams.new(thing, method_name, subpath)
|
||||||
gut.get_stubber().add_stub(sp)
|
gut.get_stubber().add_stub(sp)
|
||||||
return sp
|
return sp
|
||||||
|
@ -1613,8 +1658,8 @@ func stub(thing, p2, p3 = null):
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# convenience wrapper.
|
# convenience wrapper.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func simulate(obj, times, delta):
|
func simulate(obj, times, delta, check_is_processing: bool = false):
|
||||||
gut.simulate(obj, times, delta)
|
gut.simulate(obj, times, delta, check_is_processing)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -1670,11 +1715,12 @@ func use_parameters(params):
|
||||||
ph = _utils.ParameterHandler.new(params)
|
ph = _utils.ParameterHandler.new(params)
|
||||||
gut.parameter_handler = ph
|
gut.parameter_handler = ph
|
||||||
|
|
||||||
var output = str(
|
# DO NOT use gut.gd's get_call_count_text here since it decrements the
|
||||||
"(call #", ph.get_call_count() + 1, ") with parameters: ", ph.get_current_parameters()
|
# get_call_count value. This method increments the call count in its
|
||||||
)
|
# return statement.
|
||||||
_lgr.log(output)
|
var output = str("- params[", ph.get_call_count(), "]", "(", ph.get_current_parameters(), ")")
|
||||||
_lgr.inc_indent()
|
gut.p(output, gut.LOG_LEVEL_TEST_AND_FAILURES)
|
||||||
|
|
||||||
return ph.next_parameters()
|
return ph.next_parameters()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1729,7 +1775,8 @@ func is_passing():
|
||||||
and !["before_all", "after_all"].has(gut.get_current_test_object().name)
|
and !["before_all", "after_all"].has(gut.get_current_test_object().name)
|
||||||
):
|
):
|
||||||
return (
|
return (
|
||||||
gut.get_current_test_object().passed and gut.get_current_test_object().assert_count > 0
|
gut.get_current_test_object().is_passing()
|
||||||
|
and gut.get_current_test_object().assert_count > 0
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_lgr.error("No current test object found. is_passing must be called inside a test.")
|
_lgr.error("No current test object found. is_passing must be called inside a test.")
|
||||||
|
@ -1744,7 +1791,7 @@ func is_failing():
|
||||||
gut.get_current_test_object() != null
|
gut.get_current_test_object() != null
|
||||||
and !["before_all", "after_all"].has(gut.get_current_test_object().name)
|
and !["before_all", "after_all"].has(gut.get_current_test_object().name)
|
||||||
):
|
):
|
||||||
return !gut.get_current_test_object().passed
|
return gut.get_current_test_object().is_failing()
|
||||||
else:
|
else:
|
||||||
_lgr.error("No current test object found. is_failing must be called inside a test.")
|
_lgr.error("No current test object found. is_failing must be called inside a test.")
|
||||||
return null
|
return null
|
||||||
|
@ -1777,14 +1824,14 @@ func compare_deep(v1, v2, max_differences = null):
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Peforms a shallow compare on both values, a CompareResult instnace is returned.
|
# REMOVED
|
||||||
# The optional max_differences paramter sets the max_differences to be displayed.
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func compare_shallow(v1, v2, max_differences = null):
|
func compare_shallow(v1, v2, max_differences = null):
|
||||||
var result = _compare.shallow(v1, v2)
|
_fail("compare_shallow has been removed. Use compare_deep or just compare using == instead.")
|
||||||
if max_differences != null:
|
_lgr.error(
|
||||||
result.max_differences = max_differences
|
"compare_shallow has been removed. Use compare_deep or just compare using == instead."
|
||||||
return result
|
)
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -1810,25 +1857,36 @@ func assert_ne_deep(v1, v2):
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Performs a shallow compare and asserts the values are equal
|
# REMOVED
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func assert_eq_shallow(v1, v2):
|
func assert_eq_shallow(v1, v2):
|
||||||
var result = compare_shallow(v1, v2)
|
_fail("assert_eq_shallow has been removed. Use assert_eq/assert_same/assert_eq_deep")
|
||||||
if result.are_equal:
|
|
||||||
_pass(result.get_short_summary())
|
|
||||||
else:
|
|
||||||
_fail(result.summary)
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Performs a shallow compare and asserts the values are not equal
|
# REMOVED
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
func assert_ne_shallow(v1, v2):
|
func assert_ne_shallow(v1, v2):
|
||||||
var result = compare_shallow(v1, v2)
|
_fail("assert_eq_shallow has been removed. Use assert_eq/assert_same/assert_eq_deep")
|
||||||
if !result.are_equal:
|
|
||||||
_pass(result.get_short_summary())
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Assert wrapper for is_same
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
func assert_same(v1, v2, text = ""):
|
||||||
|
var disp = "[" + _str(v1) + "] expected to be same as [" + _str(v2) + "]: " + text
|
||||||
|
if is_same(v1, v2):
|
||||||
|
_pass(disp)
|
||||||
else:
|
else:
|
||||||
_fail(result.get_short_summary())
|
_fail(disp)
|
||||||
|
|
||||||
|
|
||||||
|
func assert_not_same(v1, v2, text = ""):
|
||||||
|
var disp = "[" + _str(v1) + "] expected to not be same as [" + _str(v2) + "]: " + text
|
||||||
|
if is_same(v1, v2):
|
||||||
|
_fail(disp)
|
||||||
|
else:
|
||||||
|
_pass(disp)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,148 +1,21 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Used to keep track of info about each test ran.
|
# This class handles calling out to the test parser and maintaining an array of
|
||||||
# ------------------------------------------------------------------------------
|
# collected_script.gd. This is used for both calling the tests and tracking
|
||||||
class Test:
|
# the results of each script and test's execution.
|
||||||
# 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 number of arguments the method has
|
|
||||||
var arg_count = 0
|
|
||||||
# The number of asserts in the test
|
|
||||||
var assert_count = 0
|
|
||||||
# if the test has been marked pending at anypont during
|
|
||||||
# execution.
|
|
||||||
var pending = false
|
|
||||||
# the line number when the test fails
|
|
||||||
var line_number = -1
|
|
||||||
# Set this to true to prevent GUT from running the test.
|
|
||||||
var should_skip = false
|
|
||||||
|
|
||||||
func did_pass():
|
|
||||||
return passed and !pending and assert_count > 0
|
|
||||||
|
|
||||||
func did_assert():
|
|
||||||
return assert_count > 0 or pending
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# This holds all the meta information for a test script. It contains the
|
|
||||||
# name of the inner class and an array of Test "structs".
|
|
||||||
#
|
#
|
||||||
# This class also facilitates all the exporting and importing of tests.
|
# This also handles exporting and importing tests.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class TestScript:
|
var CollectedScript = load("res://addons/gut/collected_script.gd")
|
||||||
var inner_class_name: StringName
|
var CollectedTest = load("res://addons/gut/collected_test.gd")
|
||||||
var tests = []
|
|
||||||
var path: String
|
|
||||||
var _utils = null
|
|
||||||
var _lgr = null
|
|
||||||
var is_loaded = false
|
|
||||||
|
|
||||||
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():
|
|
||||||
return load_script().new()
|
|
||||||
|
|
||||||
func load_script():
|
|
||||||
var to_return = load(path)
|
|
||||||
|
|
||||||
if inner_class_name != null and inner_class_name != "":
|
|
||||||
# If we wanted to do inner classes in inner classses
|
|
||||||
# then this would have to become some kind of loop or recursive
|
|
||||||
# call to go all the way down the chain or this class would
|
|
||||||
# have to change to hold onto the loaded class instead of
|
|
||||||
# just path information.
|
|
||||||
to_return = to_return.get(inner_class_name)
|
|
||||||
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
func get_filename_and_inner():
|
|
||||||
var to_return = get_filename()
|
|
||||||
if inner_class_name != "":
|
|
||||||
to_return += "." + String(inner_class_name)
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
func get_full_name():
|
|
||||||
var to_return = path
|
|
||||||
if inner_class_name != "":
|
|
||||||
to_return += "." + String(inner_class_name)
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
func get_filename():
|
|
||||||
return path.get_file()
|
|
||||||
|
|
||||||
func has_inner_class():
|
|
||||||
return inner_class_name != ""
|
|
||||||
|
|
||||||
# Note: although this no longer needs to export the inner_class names since
|
|
||||||
# they are pulled from metadata now, it is easier to leave that in
|
|
||||||
# so we don't have to cut the export down to unique script names.
|
|
||||||
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(source_path):
|
|
||||||
var to_return = source_path
|
|
||||||
if !_utils.file_exists(source_path):
|
|
||||||
_lgr.debug("Checking for remap for: " + source_path)
|
|
||||||
var remap_path = source_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)
|
|
||||||
# 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 = StringName("")
|
|
||||||
|
|
||||||
func get_test_named(name):
|
|
||||||
return _utils.search_array(tests, "name", name)
|
|
||||||
|
|
||||||
func mark_tests_to_skip_with_suffix(suffix):
|
|
||||||
for single_test in tests:
|
|
||||||
single_test.should_skip = single_test.name.ends_with(suffix)
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# start test_collector, I don't think I like the name.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
var scripts = []
|
|
||||||
var _test_prefix = "test_"
|
var _test_prefix = "test_"
|
||||||
var _test_class_prefix = "Test"
|
var _test_class_prefix = "Test"
|
||||||
|
|
||||||
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
var _utils = load("res://addons/gut/utils.gd").get_instance()
|
||||||
var _lgr = _utils.get_logger()
|
var _lgr = _utils.get_logger()
|
||||||
|
|
||||||
|
# Array of CollectedScripts.
|
||||||
|
var scripts = []
|
||||||
|
|
||||||
|
|
||||||
func _does_inherit_from_test(thing):
|
func _does_inherit_from_test(thing):
|
||||||
var base_script = thing.get_base_script()
|
var base_script = thing.get_base_script()
|
||||||
|
@ -156,7 +29,7 @@ func _does_inherit_from_test(thing):
|
||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
func _populate_tests(test_script: TestScript):
|
func _populate_tests(test_script):
|
||||||
var script = test_script.load_script()
|
var script = test_script.load_script()
|
||||||
if script == null:
|
if script == null:
|
||||||
print(" !!! ", test_script.path, " could not be loaded")
|
print(" !!! ", test_script.path, " could not be loaded")
|
||||||
|
@ -167,7 +40,7 @@ func _populate_tests(test_script: TestScript):
|
||||||
for i in range(methods.size()):
|
for i in range(methods.size()):
|
||||||
var name = methods[i]["name"]
|
var name = methods[i]["name"]
|
||||||
if name.begins_with(_test_prefix):
|
if name.begins_with(_test_prefix):
|
||||||
var t = Test.new()
|
var t = CollectedTest.new()
|
||||||
t.name = name
|
t.name = name
|
||||||
t.arg_count = methods[i]["args"].size()
|
t.arg_count = methods[i]["args"].size()
|
||||||
test_script.tests.append(t)
|
test_script.tests.append(t)
|
||||||
|
@ -184,11 +57,7 @@ func _get_inner_test_class_names(loaded):
|
||||||
inner_classes.append(key)
|
inner_classes.append(key)
|
||||||
else:
|
else:
|
||||||
_lgr.warn(
|
_lgr.warn(
|
||||||
str(
|
str("Ignoring Inner Class ", key, " because it does not extend GutTest")
|
||||||
"Ignoring Inner Class ",
|
|
||||||
key,
|
|
||||||
" because it does not extend res://addons/gut/test.gd"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# This could go deeper and find inner classes within inner classes
|
# This could go deeper and find inner classes within inner classes
|
||||||
|
@ -208,11 +77,13 @@ func _parse_script(test_script):
|
||||||
_populate_tests(test_script)
|
_populate_tests(test_script)
|
||||||
scripts_found.append(test_script.path)
|
scripts_found.append(test_script.path)
|
||||||
inner_classes = _get_inner_test_class_names(loaded)
|
inner_classes = _get_inner_test_class_names(loaded)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
for i in range(inner_classes.size()):
|
for i in range(inner_classes.size()):
|
||||||
var loaded_inner = loaded.get(inner_classes[i])
|
var loaded_inner = loaded.get(inner_classes[i])
|
||||||
if _does_inherit_from_test(loaded_inner):
|
if _does_inherit_from_test(loaded_inner):
|
||||||
var ts = TestScript.new(_utils, _lgr)
|
var ts = CollectedScript.new(_utils, _lgr)
|
||||||
ts.path = test_script.path
|
ts.path = test_script.path
|
||||||
ts.inner_class_name = inner_classes[i]
|
ts.inner_class_name = inner_classes[i]
|
||||||
_populate_tests(ts)
|
_populate_tests(ts)
|
||||||
|
@ -226,7 +97,6 @@ func _parse_script(test_script):
|
||||||
# Public
|
# Public
|
||||||
# -----------------
|
# -----------------
|
||||||
func add_script(path):
|
func add_script(path):
|
||||||
# print('Adding ', path)
|
|
||||||
# SHORTCIRCUIT
|
# SHORTCIRCUIT
|
||||||
if has_script(path):
|
if has_script(path):
|
||||||
return []
|
return []
|
||||||
|
@ -236,10 +106,21 @@ func add_script(path):
|
||||||
_lgr.error("Could not find script: " + path)
|
_lgr.error("Could not find script: " + path)
|
||||||
return
|
return
|
||||||
|
|
||||||
var ts = TestScript.new(_utils, _lgr)
|
var ts = CollectedScript.new(_utils, _lgr)
|
||||||
ts.path = path
|
ts.path = path
|
||||||
|
# Append right away because if we don't test_doubler.gd.TestInitParameters
|
||||||
|
# will HARD crash. I couldn't figure out what was causing the issue but
|
||||||
|
# appending right away, and then removing if it's not valid seems to fix
|
||||||
|
# things. It might have to do with the ordering of the test classes in
|
||||||
|
# the test collecter. I'm not really sure.
|
||||||
scripts.append(ts)
|
scripts.append(ts)
|
||||||
return _parse_script(ts)
|
var parse_results = _parse_script(ts)
|
||||||
|
|
||||||
|
if parse_results.find(path) == -1:
|
||||||
|
_lgr.warn(str("Ignoring script ", path, " because it does not extend GutTest"))
|
||||||
|
scripts.remove_at(scripts.find(ts))
|
||||||
|
|
||||||
|
return parse_results
|
||||||
|
|
||||||
|
|
||||||
func clear():
|
func clear():
|
||||||
|
@ -261,7 +142,7 @@ func export_tests(path):
|
||||||
var success = true
|
var success = true
|
||||||
var f = ConfigFile.new()
|
var f = ConfigFile.new()
|
||||||
for i in range(scripts.size()):
|
for i in range(scripts.size()):
|
||||||
scripts[i].export_to(f, str("TestScript-", i))
|
scripts[i].export_to(f, str("CollectedScript-", i))
|
||||||
var result = f.save(path)
|
var result = f.save(path)
|
||||||
if result != OK:
|
if result != OK:
|
||||||
_lgr.error(str("Could not save exported tests to [", path, "]. Error code: ", result))
|
_lgr.error(str("Could not save exported tests to [", path, "]. Error code: ", result))
|
||||||
|
@ -278,7 +159,7 @@ func import_tests(path):
|
||||||
else:
|
else:
|
||||||
var sections = f.get_sections()
|
var sections = f.get_sections()
|
||||||
for key in sections:
|
for key in sections:
|
||||||
var ts = TestScript.new(_utils, _lgr)
|
var ts = CollectedScript.new(_utils, _lgr)
|
||||||
ts.import_from(f, key)
|
ts.import_from(f, key)
|
||||||
_populate_tests(ts)
|
_populate_tests(ts)
|
||||||
scripts.append(ts)
|
scripts.append(ts)
|
||||||
|
@ -330,3 +211,57 @@ func get_test_class_prefix():
|
||||||
|
|
||||||
func set_test_class_prefix(test_class_prefix):
|
func set_test_class_prefix(test_class_prefix):
|
||||||
_test_class_prefix = test_class_prefix
|
_test_class_prefix = test_class_prefix
|
||||||
|
|
||||||
|
|
||||||
|
func get_scripts():
|
||||||
|
return scripts
|
||||||
|
|
||||||
|
|
||||||
|
func get_ran_test_count():
|
||||||
|
var count = 0
|
||||||
|
for s in scripts:
|
||||||
|
count += s.get_ran_test_count()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_ran_script_count():
|
||||||
|
var count = 0
|
||||||
|
for s in scripts:
|
||||||
|
if s.was_run:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_test_count():
|
||||||
|
var count = 0
|
||||||
|
for s in scripts:
|
||||||
|
count += s.tests.size()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_assert_count():
|
||||||
|
var count = 0
|
||||||
|
for s in scripts:
|
||||||
|
count += s.get_assert_count()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_pass_count():
|
||||||
|
var count = 0
|
||||||
|
for s in scripts:
|
||||||
|
count += s.get_pass_count()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_fail_count():
|
||||||
|
var count = 0
|
||||||
|
for s in scripts:
|
||||||
|
count += s.get_fail_count()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
func get_pending_count():
|
||||||
|
var count = 0
|
||||||
|
for s in scripts:
|
||||||
|
count += s.get_pending_count()
|
||||||
|
return count
|
||||||
|
|
|
@ -1,44 +1,41 @@
|
||||||
# ##############################################################################
|
class_name GutUtils
|
||||||
#(G)odot (U)nit (T)est class
|
# ------------------------------------------------------------------------------
|
||||||
#
|
|
||||||
# ##############################################################################
|
|
||||||
# The MIT License (MIT)
|
|
||||||
# =====================
|
|
||||||
#
|
|
||||||
# Copyright (c) 2020 Tom "Butch" Wesley
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
#
|
|
||||||
# ##############################################################################
|
|
||||||
# Description
|
# Description
|
||||||
# -----------
|
# -----------
|
||||||
# This class is a PSUEDO SINGLETON. You should not make instances of it but use
|
# This class is a PSUEDO SINGLETON. You should not make instances of it but use
|
||||||
# the get_instance static method.
|
# the get_instance static method.
|
||||||
# ##############################################################################
|
|
||||||
extends Node
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# The instance name as a function since you can't have static variables.
|
# NOTE: I think this can become completely static now that we have static
|
||||||
|
# variables. A lot would have to change though. But it would be good
|
||||||
|
# to do.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
static func INSTANCE_NAME():
|
const GUT_METADATA = "__gutdbl"
|
||||||
return "__GutUtilsInstName__"
|
|
||||||
|
# Note, these cannot change since places are checking for TYPE_INT to determine
|
||||||
|
# how to process parameters.
|
||||||
|
enum DOUBLE_STRATEGY {
|
||||||
|
INCLUDE_NATIVE,
|
||||||
|
SCRIPT_ONLY,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DIFF { DEEP, SIMPLE }
|
||||||
|
|
||||||
|
const TEST_STATUSES = {
|
||||||
|
NO_ASSERTS = "no asserts",
|
||||||
|
SKIPPED = "skipped",
|
||||||
|
NOT_RUN = "not run",
|
||||||
|
PENDING = "pending",
|
||||||
|
# These two got the "ed" b/c pass is a reserved word and I could not
|
||||||
|
# think of better words.
|
||||||
|
FAILED = "fail",
|
||||||
|
PASSED = "pass"
|
||||||
|
}
|
||||||
|
|
||||||
|
# This is a holdover from when GUT was making a psuedo autoload. It would add
|
||||||
|
# an instance of this class to the tree with a name and retrieve it when
|
||||||
|
# get_instance was called. We now have static variables so this var is now
|
||||||
|
# used instead of a node.
|
||||||
|
static var _the_instance = null
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -61,17 +58,91 @@ static func get_root_node():
|
||||||
# running.
|
# running.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
static func get_instance():
|
static func get_instance():
|
||||||
var the_root = get_root_node()
|
if _the_instance == null:
|
||||||
var inst = null
|
_the_instance = GutUtils.new()
|
||||||
if the_root.has_node(INSTANCE_NAME()):
|
|
||||||
inst = the_root.get_node(INSTANCE_NAME())
|
return _the_instance
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Gets the value from an enum. If passed an int it will return it if the enum
|
||||||
|
# contains it. If passed a string it will convert it to upper case and replace
|
||||||
|
# spaces with underscores. If the enum contains the key, it will return the
|
||||||
|
# value for they key. When keys or ints are not found, the default is returned.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
static func get_enum_value(thing, e, default = null):
|
||||||
|
var to_return = default
|
||||||
|
|
||||||
|
if typeof(thing) == TYPE_STRING:
|
||||||
|
var converted = thing.to_upper().replace(" ", "_")
|
||||||
|
if e.keys().has(converted):
|
||||||
|
to_return = e[converted]
|
||||||
else:
|
else:
|
||||||
inst = load("res://addons/gut/utils.gd").new()
|
if e.values().has(thing):
|
||||||
inst.set_name(INSTANCE_NAME())
|
to_return = thing
|
||||||
the_root.add_child(inst)
|
|
||||||
return inst
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# return if_null if value is null otherwise return value
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
static func nvl(value, if_null):
|
||||||
|
if value == null:
|
||||||
|
return if_null
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
static func pretty_print(dict):
|
||||||
|
print(JSON.stringify(dict, " "))
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
static func print_properties(props, thing, print_all_meta = false):
|
||||||
|
for i in range(props.size()):
|
||||||
|
var prop_name = props[i].name
|
||||||
|
var prop_value = thing.get(props[i].name)
|
||||||
|
var print_value = str(prop_value)
|
||||||
|
if print_value.length() > 100:
|
||||||
|
print_value = print_value.substr(0, 97) + "..."
|
||||||
|
elif print_value == "":
|
||||||
|
print_value = "EMPTY"
|
||||||
|
|
||||||
|
print(prop_name, " = ", print_value)
|
||||||
|
if print_all_meta:
|
||||||
|
print(" ", props[i])
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Gets the value of the node_property 'script' from a PackedScene's root node.
|
||||||
|
# This does not assume the location of the root node in the PackedScene's node
|
||||||
|
# list. This also does not assume the index of the 'script' node property in
|
||||||
|
# a nodes's property list.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
static func get_scene_script_object(scene):
|
||||||
|
var state = scene.get_state()
|
||||||
|
var to_return = null
|
||||||
|
var root_node_path = NodePath(".")
|
||||||
|
var node_idx = 0
|
||||||
|
|
||||||
|
while node_idx < state.get_node_count() and to_return == null:
|
||||||
|
if state.get_node_path(node_idx) == root_node_path:
|
||||||
|
for i in range(state.get_node_property_count(node_idx)):
|
||||||
|
if state.get_node_property_name(node_idx, i) == "script":
|
||||||
|
to_return = state.get_node_property_value(node_idx, i)
|
||||||
|
|
||||||
|
node_idx += 1
|
||||||
|
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# ##############################################################################
|
||||||
|
# Start Class
|
||||||
|
# ##############################################################################
|
||||||
var Logger = load("res://addons/gut/logger.gd") # everything should use get_logger
|
var Logger = load("res://addons/gut/logger.gd") # everything should use get_logger
|
||||||
var _lgr = null
|
var _lgr = null
|
||||||
var json = JSON.new()
|
var json = JSON.new()
|
||||||
|
@ -85,6 +156,7 @@ var CompareResult = load("res://addons/gut/compare_result.gd")
|
||||||
var DiffTool = load("res://addons/gut/diff_tool.gd")
|
var DiffTool = load("res://addons/gut/diff_tool.gd")
|
||||||
var Doubler = load("res://addons/gut/doubler.gd")
|
var Doubler = load("res://addons/gut/doubler.gd")
|
||||||
var Gut = load("res://addons/gut/gut.gd")
|
var Gut = load("res://addons/gut/gut.gd")
|
||||||
|
var GutConfig = load("res://addons/gut/gut_config.gd")
|
||||||
var HookScript = load("res://addons/gut/hook_script.gd")
|
var HookScript = load("res://addons/gut/hook_script.gd")
|
||||||
var InnerClassRegistry = load("res://addons/gut/inner_class_registry.gd")
|
var InnerClassRegistry = load("res://addons/gut/inner_class_registry.gd")
|
||||||
var InputFactory = load("res://addons/gut/input_factory.gd")
|
var InputFactory = load("res://addons/gut/input_factory.gd")
|
||||||
|
@ -106,69 +178,15 @@ var Summary = load("res://addons/gut/summary.gd")
|
||||||
var Test = load("res://addons/gut/test.gd")
|
var Test = load("res://addons/gut/test.gd")
|
||||||
var TestCollector = load("res://addons/gut/test_collector.gd")
|
var TestCollector = load("res://addons/gut/test_collector.gd")
|
||||||
var ThingCounter = load("res://addons/gut/thing_counter.gd")
|
var ThingCounter = load("res://addons/gut/thing_counter.gd")
|
||||||
|
var CollectedTest = load("res://addons/gut/collected_test.gd")
|
||||||
|
var CollectedScript = load("res://addons/gut/collected_test.gd")
|
||||||
|
|
||||||
|
var GutScene = load("res://addons/gut/GutScene.tscn")
|
||||||
|
|
||||||
# Source of truth for the GUT version
|
# Source of truth for the GUT version
|
||||||
var version = "7.4.1"
|
var version = "9.1.1"
|
||||||
# The required Godot version as an array.
|
# The required Godot version as an array.
|
||||||
var req_godot = [3, 2, 0]
|
var req_godot = [4, 1, 0]
|
||||||
|
|
||||||
# Online fetch of the latest version available on github
|
|
||||||
var latest_version = null
|
|
||||||
var should_display_latest_version = false
|
|
||||||
|
|
||||||
# These methods all call super implicitly. Stubbing them to call super causes
|
|
||||||
# super to be called twice.
|
|
||||||
var non_super_methods = [
|
|
||||||
"_init",
|
|
||||||
"_ready",
|
|
||||||
"_notification",
|
|
||||||
"_enter_world",
|
|
||||||
"_exit_world",
|
|
||||||
"_process",
|
|
||||||
"_physics_process",
|
|
||||||
"_exit_tree",
|
|
||||||
"_gui_input ",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
_http_request_latest_version()
|
|
||||||
|
|
||||||
|
|
||||||
func _http_request_latest_version() -> void:
|
|
||||||
return
|
|
||||||
var http_request = HTTPRequest.new()
|
|
||||||
http_request.name = "http_request"
|
|
||||||
add_child(http_request)
|
|
||||||
http_request.connect(
|
|
||||||
"request_completed", Callable(self, "_on_http_request_latest_version_completed")
|
|
||||||
)
|
|
||||||
# Perform a GET request. The URL below returns JSON as of writing.
|
|
||||||
var __error = http_request.request("https://api.github.com/repos/bitwes/Gut/releases/latest")
|
|
||||||
|
|
||||||
|
|
||||||
func _on_http_request_latest_version_completed(result, response_code, headers, body):
|
|
||||||
if not result == HTTPRequest.RESULT_SUCCESS:
|
|
||||||
return
|
|
||||||
|
|
||||||
var test_json_conv = JSON.new()
|
|
||||||
test_json_conv.parse(body.get_string_from_utf8())
|
|
||||||
var response = test_json_conv.get_data()
|
|
||||||
# Will print the user agent string used by the HTTPRequest node (as recognized by httpbin.org).
|
|
||||||
if response:
|
|
||||||
if response.get("html_url"):
|
|
||||||
latest_version = Array(response.html_url.split("/")).pop_back().right(1)
|
|
||||||
if latest_version != version:
|
|
||||||
should_display_latest_version = true
|
|
||||||
|
|
||||||
|
|
||||||
const GUT_METADATA = "__gutdbl"
|
|
||||||
|
|
||||||
# Note, these cannot change since places are checking for TYPE_INT to determine
|
|
||||||
# how to process parameters.
|
|
||||||
enum DOUBLE_STRATEGY { SCRIPT_ONLY, INCLUDE_SUPER }
|
|
||||||
|
|
||||||
enum DIFF { DEEP, SHALLOW, SIMPLE }
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -256,16 +274,6 @@ func get_logger():
|
||||||
return _lgr
|
return _lgr
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# 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
|
# returns true if the object has been freed, false if not
|
||||||
#
|
#
|
||||||
|
@ -443,10 +451,6 @@ func are_datatypes_same(got, expected):
|
||||||
return !(typeof(got) != typeof(expected) and got != null and expected != null)
|
return !(typeof(got) != typeof(expected) and got != null and expected != null)
|
||||||
|
|
||||||
|
|
||||||
func pretty_print(dict):
|
|
||||||
print(json.stringify(dict, " "))
|
|
||||||
|
|
||||||
|
|
||||||
func get_script_text(obj):
|
func get_script_text(obj):
|
||||||
return obj.get_script().get_source_code()
|
return obj.get_script().get_source_code()
|
||||||
|
|
||||||
|
@ -514,23 +518,34 @@ func create_script_from_source(source, override_path = null):
|
||||||
return DynamicScript
|
return DynamicScript
|
||||||
|
|
||||||
|
|
||||||
func get_scene_script_object(scene):
|
func get_display_size():
|
||||||
var state = scene.get_state()
|
return Engine.get_main_loop().get_viewport().get_visible_rect()
|
||||||
var to_return = null
|
|
||||||
var root_node_path = NodePath(".")
|
|
||||||
var node_idx = 0
|
|
||||||
|
|
||||||
while node_idx < state.get_node_count() and to_return == null:
|
# ##############################################################################
|
||||||
# Assumes that the first node we encounter that has a root node path, one
|
#(G)odot (U)nit (T)est class
|
||||||
# property, and that property is named 'script' is the GDScript for the
|
#
|
||||||
# scene. This could be flawed.
|
# ##############################################################################
|
||||||
if (
|
# The MIT License (MIT)
|
||||||
state.get_node_path(node_idx) == root_node_path
|
# =====================
|
||||||
and state.get_node_property_count(node_idx) == 1
|
#
|
||||||
):
|
# Copyright (c) 2023 Tom "Butch" Wesley
|
||||||
if state.get_node_property_name(node_idx, 0) == "script":
|
#
|
||||||
to_return = state.get_node_property_value(node_idx, 0)
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
node_idx += 1
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
return to_return
|
# 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.
|
||||||
|
#
|
||||||
|
# ##############################################################################
|
||||||
|
|
|
@ -8,8 +8,8 @@ var env: Dictionary
|
||||||
|
|
||||||
|
|
||||||
func before_each():
|
func before_each():
|
||||||
assert(OS.set_environment(EMPTY_VAR, ""))
|
OS.set_environment(EMPTY_VAR, "")
|
||||||
assert(OS.set_environment(TEST_VAR, TEST_VAL))
|
OS.set_environment(TEST_VAR, TEST_VAL)
|
||||||
env = LibuvUtils.get_os_environ()
|
env = LibuvUtils.get_os_environ()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue