mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-23 02:00:25 +01:00
323 lines
8.9 KiB
GDScript
323 lines
8.9 KiB
GDScript
|
@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.
|
||
|
#
|
||
|
# ##############################################################################
|