- 修复 WebSocketManager/SocketIOClient 函数缩进错误 - 重命名 is_connected() 避免与 Object 基类冲突 - 修复 tscn 文件多余前导空格 - 修复测试文件 GUT 断言函数调用 - 添加 GUT 测试框架
544 lines
15 KiB
GDScript
544 lines
15 KiB
GDScript
@tool
|
|
extends Control
|
|
|
|
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
|
var GutConfigGui = load('res://addons/gut/gui/gut_config_gui.gd')
|
|
var AboutWindow = load("res://addons/gut/gui/about.tscn")
|
|
|
|
var _interface = null;
|
|
var _is_running = false :
|
|
set(val):
|
|
_is_running = val
|
|
_disable_run_buttons(_is_running)
|
|
|
|
var _gut_config = load('res://addons/gut/gut_config.gd').new()
|
|
var _gut_config_gui = null
|
|
var _gut_plugin = null
|
|
var _light_color = Color(0, 0, 0, .5) :
|
|
set(val):
|
|
_light_color = val
|
|
if(is_inside_tree()):
|
|
_ctrls.light.queue_redraw()
|
|
var _panel_button = null
|
|
var _user_prefs = null
|
|
var _shell_out_panel = null
|
|
|
|
|
|
var menu_manager = null :
|
|
set(val):
|
|
menu_manager = val
|
|
if(val != null):
|
|
_apply_shortcuts()
|
|
menu_manager.toggle_windowed.connect(_on_toggle_windowed)
|
|
menu_manager.about.connect(show_about)
|
|
menu_manager.run_all.connect(_run_all)
|
|
menu_manager.show_gut.connect(_on_show_gut)
|
|
|
|
|
|
@onready var _ctrls = {
|
|
about = %ExtraButtons/About,
|
|
light = %StatusIndicator,
|
|
output_button = %ExtraButtons/OutputBtn,
|
|
run_button = $layout/ControlBar/RunAll,
|
|
run_externally_dialog = $ShellOutOptions,
|
|
run_mode = %ExtraButtons/RunMode,
|
|
run_at_cursor = $layout/ControlBar/RunAtCursor,
|
|
run_results_button = %ExtraButtons/RunResultsBtn,
|
|
settings = $layout/RSplit/sc/Settings,
|
|
settings_button = %ExtraButtons/Settings,
|
|
shortcut_dialog = $ShortcutDialog,
|
|
shortcuts_button = %ExtraButtons/Shortcuts,
|
|
|
|
results = {
|
|
bar = $layout/ControlBar2,
|
|
errors = %errors_value,
|
|
failing = %failing_value,
|
|
orphans = %orphans_value,
|
|
passing = %passing_value,
|
|
pending = %pending_value,
|
|
warnings = %warnings_value,
|
|
},
|
|
}
|
|
|
|
@onready var results_v_split = %VSplitResults
|
|
@onready var results_h_split = %HSplitResults
|
|
@onready var results_tree = %RunResults
|
|
@onready var results_text = %OutputText
|
|
@onready var make_floating_btn = %MakeFloating
|
|
|
|
|
|
func _ready():
|
|
if(get_parent() is SubViewport):
|
|
return
|
|
|
|
GutEditorGlobals.create_temp_directory()
|
|
|
|
_user_prefs = GutEditorGlobals.user_prefs
|
|
_gut_config_gui = GutConfigGui.new(_ctrls.settings)
|
|
|
|
_ctrls.results.bar.connect('draw', _on_results_bar_draw.bind(_ctrls.results.bar))
|
|
hide_settings(!_ctrls.settings_button.button_pressed)
|
|
|
|
_gut_config.load_options(GutEditorGlobals.editor_run_gut_config_path)
|
|
_gut_config_gui.set_options(_gut_config.options)
|
|
|
|
_ctrls.shortcuts_button.icon = get_theme_icon('Shortcut', 'EditorIcons')
|
|
_ctrls.settings_button.icon = get_theme_icon('Tools', 'EditorIcons')
|
|
_ctrls.run_results_button.icon = get_theme_icon('AnimationTrackGroup', 'EditorIcons') # Tree
|
|
_ctrls.output_button.icon = get_theme_icon('Font', 'EditorIcons')
|
|
make_floating_btn.icon = get_theme_icon("MakeFloating", 'EditorIcons')
|
|
make_floating_btn.text = ''
|
|
_ctrls.about.icon = get_theme_icon('Info', 'EditorIcons')
|
|
_ctrls.about.text = ''
|
|
_ctrls.run_mode.icon = get_theme_icon("ViewportSpeed", 'EditorIcons')
|
|
|
|
results_tree.set_output_control(results_text)
|
|
|
|
var check_import = load('res://addons/gut/images/HSplitContainer.svg')
|
|
if(check_import == null):
|
|
results_tree.add_centered_text("GUT got some new images that are not imported yet. Please restart Godot.")
|
|
print('GUT got some new images that are not imported yet. Please restart Godot.')
|
|
else:
|
|
results_tree.add_centered_text("Let's run some tests!")
|
|
|
|
_ctrls.run_externally_dialog.load_from_file()
|
|
_apply_options_to_controls()
|
|
|
|
results_vert_layout()
|
|
|
|
|
|
func _process(_delta):
|
|
if(_is_running):
|
|
if(_ctrls.run_externally_dialog.should_run_externally()):
|
|
if(!is_instance_valid(_shell_out_panel)):
|
|
_is_running = false
|
|
show_me()
|
|
elif(!_interface.is_playing_scene()):
|
|
_is_running = false
|
|
results_text.add_text("\ndone")
|
|
load_result_output()
|
|
show_me()
|
|
|
|
|
|
# ---------------
|
|
# Private
|
|
# ---------------
|
|
func _apply_options_to_controls():
|
|
hide_settings(_user_prefs.hide_settings.value)
|
|
hide_result_tree(_user_prefs.hide_result_tree.value)
|
|
hide_output_text(_user_prefs.hide_output_text.value)
|
|
results_tree.set_show_orphans(!_gut_config.options.hide_orphans)
|
|
var shell_dialog_size = _user_prefs.run_externally_options_dialog_size.value
|
|
|
|
if(shell_dialog_size != Vector2i(-1, -1)):
|
|
_ctrls.run_externally_dialog.size = Vector2i(shell_dialog_size)
|
|
|
|
if(_user_prefs.shortcuts_dialog_size.value != Vector2i(-1, -1)):
|
|
_ctrls.shortcut_dialog.size = _user_prefs.shortcuts_dialog_size.value
|
|
|
|
var mode_ind = 'Ed'
|
|
if(_ctrls.run_externally_dialog.run_mode == _ctrls.run_externally_dialog.RUN_MODE_BLOCKING):
|
|
mode_ind = 'ExB'
|
|
elif(_ctrls.run_externally_dialog.run_mode == _ctrls.run_externally_dialog.RUN_MODE_NON_BLOCKING):
|
|
mode_ind = 'ExN'
|
|
_ctrls.run_mode.text = mode_ind
|
|
|
|
_ctrls.run_at_cursor.apply_gut_config(_gut_config)
|
|
|
|
|
|
|
|
func _disable_run_buttons(should):
|
|
_ctrls.run_button.disabled = should
|
|
_ctrls.run_at_cursor.disabled = should
|
|
|
|
|
|
func _is_test_script(script):
|
|
var from = script.get_base_script()
|
|
while(from and from.resource_path != 'res://addons/gut/test.gd'):
|
|
from = from.get_base_script()
|
|
|
|
return from != null
|
|
|
|
|
|
func _show_errors(errs):
|
|
results_text.clear()
|
|
var text = "Cannot run tests, you have a configuration error:\n"
|
|
for e in errs:
|
|
text += str('* ', e, "\n")
|
|
text += "Check your settings ----->"
|
|
results_text.add_text(text)
|
|
hide_output_text(false)
|
|
hide_settings(false)
|
|
|
|
|
|
func _save_user_prefs():
|
|
_user_prefs.hide_settings.value = !_ctrls.settings_button.button_pressed
|
|
_user_prefs.hide_result_tree.value = !_ctrls.run_results_button.button_pressed
|
|
_user_prefs.hide_output_text.value = !_ctrls.output_button.button_pressed
|
|
_user_prefs.shortcuts_dialog_size.value = _ctrls.shortcut_dialog.size
|
|
|
|
_user_prefs.run_externally.value = _ctrls.run_externally_dialog.run_mode != _ctrls.run_externally_dialog.RUN_MODE_EDITOR
|
|
_user_prefs.run_externally_options_dialog_size.value = _ctrls.run_externally_dialog.size
|
|
|
|
_user_prefs.save_it()
|
|
|
|
|
|
func _save_config():
|
|
_save_user_prefs()
|
|
|
|
_gut_config.options = _gut_config_gui.get_options(_gut_config.options)
|
|
var w_result = _gut_config.write_options(GutEditorGlobals.editor_run_gut_config_path)
|
|
if(w_result != OK):
|
|
push_error(str('Could not write options to ', GutEditorGlobals.editor_run_gut_config_path, ': ', w_result))
|
|
else:
|
|
_gut_config_gui.mark_saved()
|
|
|
|
|
|
func _run_externally():
|
|
_shell_out_panel = GutUtils.RunExternallyScene.instantiate()
|
|
_shell_out_panel.bottom_panel = self
|
|
_shell_out_panel.blocking_mode = _ctrls.run_externally_dialog.run_mode
|
|
_shell_out_panel.additional_arguments = _ctrls.run_externally_dialog.get_additional_arguments_array()
|
|
|
|
add_child(_shell_out_panel)
|
|
_shell_out_panel.run_tests()
|
|
|
|
|
|
func _run_tests():
|
|
show_me()
|
|
if(_is_running):
|
|
push_error("GUT: Cannot run tests, tests are already running.")
|
|
return
|
|
|
|
clear_results()
|
|
GutEditorGlobals.create_temp_directory()
|
|
_light_color = Color.BLUE
|
|
|
|
var issues = _gut_config_gui.get_config_issues()
|
|
if(issues.size() > 0):
|
|
_show_errors(issues)
|
|
return
|
|
|
|
write_file(GutEditorGlobals.editor_run_bbcode_results_path, 'Run in progress')
|
|
write_file(GutEditorGlobals.editor_run_json_results_path, '')
|
|
_save_config()
|
|
_apply_options_to_controls()
|
|
|
|
results_text.clear()
|
|
results_tree.clear()
|
|
results_tree.add_centered_text('Running...')
|
|
|
|
_is_running = true
|
|
results_text.add_text('Running...')
|
|
|
|
if(_ctrls.run_externally_dialog.should_run_externally()):
|
|
_gut_plugin.make_bottom_panel_item_visible(self)
|
|
_run_externally()
|
|
else:
|
|
_interface.play_custom_scene('res://addons/gut/gui/run_from_editor.tscn')
|
|
|
|
|
|
func _apply_shortcuts():
|
|
if(menu_manager != null):
|
|
menu_manager.apply_gut_shortcuts(_ctrls.shortcut_dialog)
|
|
|
|
_ctrls.run_button.shortcut = \
|
|
_ctrls.shortcut_dialog.scbtn_run_all.get_shortcut()
|
|
_ctrls.run_at_cursor.get_script_button().shortcut = \
|
|
_ctrls.shortcut_dialog.scbtn_run_current_script.get_shortcut()
|
|
_ctrls.run_at_cursor.get_inner_button().shortcut = \
|
|
_ctrls.shortcut_dialog.scbtn_run_current_inner.get_shortcut()
|
|
_ctrls.run_at_cursor.get_test_button().shortcut = \
|
|
_ctrls.shortcut_dialog.scbtn_run_current_test.get_shortcut()
|
|
# Took this out because it seems to break using the shortcut when docked.
|
|
# Though it does allow the shortcut to work when windowed. Shortcuts
|
|
# are weird.
|
|
# make_floating_btn.shortcut = \
|
|
# _ctrls.shortcut_dialog.scbtn_windowed.get_shortcut()
|
|
|
|
|
|
if(_panel_button != null):
|
|
_panel_button.shortcut = _ctrls.shortcut_dialog.scbtn_panel.get_shortcut()
|
|
|
|
|
|
func _run_all():
|
|
_gut_config.options.selected = null
|
|
_gut_config.options.inner_class = null
|
|
_gut_config.options.unit_test_name = null
|
|
|
|
_run_tests()
|
|
|
|
|
|
# ---------------
|
|
# Events
|
|
# ---------------
|
|
func _on_results_bar_draw(bar):
|
|
bar.draw_rect(Rect2(Vector2(0, 0), bar.size), Color(0, 0, 0, .2))
|
|
|
|
|
|
func _on_Light_draw():
|
|
var l = _ctrls.light
|
|
l.draw_circle(Vector2(l.size.x / 2, l.size.y / 2), l.size.x / 2, _light_color)
|
|
|
|
|
|
func _on_RunAll_pressed():
|
|
_run_all()
|
|
|
|
|
|
func _on_Shortcuts_pressed():
|
|
_ctrls.shortcut_dialog.popup_centered()
|
|
|
|
|
|
func _on_sortcut_dialog_confirmed() -> void:
|
|
_apply_shortcuts()
|
|
_ctrls.shortcut_dialog.save_shortcuts()
|
|
_save_user_prefs()
|
|
|
|
|
|
func _on_RunAtCursor_run_tests(what):
|
|
_gut_config.options.selected = what.script
|
|
_gut_config.options.inner_class = what.inner_class
|
|
_gut_config.options.unit_test_name = what.method
|
|
|
|
_run_tests()
|
|
|
|
|
|
func _on_Settings_pressed():
|
|
hide_settings(!_ctrls.settings_button.button_pressed)
|
|
_save_config()
|
|
|
|
|
|
func _on_OutputBtn_pressed():
|
|
hide_output_text(!_ctrls.output_button.button_pressed)
|
|
_save_config()
|
|
|
|
|
|
func _on_RunResultsBtn_pressed():
|
|
hide_result_tree(! _ctrls.run_results_button.button_pressed)
|
|
_save_config()
|
|
|
|
|
|
# Currently not used, but will be when I figure out how to put
|
|
# colors into the text results
|
|
func _on_UseColors_pressed():
|
|
pass
|
|
|
|
|
|
func _on_shell_out_options_confirmed() -> void:
|
|
_ctrls.run_externally_dialog.save_to_file()
|
|
_save_user_prefs()
|
|
_apply_options_to_controls()
|
|
|
|
|
|
func _on_run_mode_pressed() -> void:
|
|
_ctrls.run_externally_dialog.popup_centered()
|
|
|
|
|
|
func _on_toggle_windowed():
|
|
_gut_plugin.toggle_windowed()
|
|
|
|
|
|
func _on_to_window_pressed() -> void:
|
|
_gut_plugin.toggle_windowed()
|
|
|
|
|
|
func _on_show_gut() -> void:
|
|
show_hide()
|
|
|
|
|
|
func _on_about_pressed() -> void:
|
|
show_about()
|
|
|
|
# ---------------
|
|
# Public
|
|
# ---------------
|
|
func load_shortcuts():
|
|
_ctrls.shortcut_dialog.load_shortcuts()
|
|
_apply_shortcuts()
|
|
|
|
|
|
func hide_result_tree(should):
|
|
results_tree.visible = !should
|
|
_ctrls.run_results_button.button_pressed = !should
|
|
|
|
|
|
func hide_settings(should):
|
|
var s_scroll = _ctrls.settings.get_parent()
|
|
s_scroll.visible = !should
|
|
|
|
# collapse only collapses the first control, so we move
|
|
# settings around to be the collapsed one
|
|
if(should):
|
|
s_scroll.get_parent().move_child(s_scroll, 0)
|
|
else:
|
|
s_scroll.get_parent().move_child(s_scroll, 1)
|
|
|
|
$layout/RSplit.collapsed = should
|
|
_ctrls.settings_button.button_pressed = !should
|
|
|
|
|
|
func hide_output_text(should):
|
|
results_text.visible = !should
|
|
_ctrls.output_button.button_pressed = !should
|
|
|
|
|
|
func clear_results():
|
|
_light_color = Color(0, 0, 0, .5)
|
|
|
|
_ctrls.results.passing.text = "0"
|
|
_ctrls.results.passing.get_parent().visible = false
|
|
|
|
_ctrls.results.failing.text = "0"
|
|
_ctrls.results.failing.get_parent().visible = false
|
|
|
|
_ctrls.results.pending.text = "0"
|
|
_ctrls.results.pending.get_parent().visible = false
|
|
|
|
_ctrls.results.errors.text = "0"
|
|
_ctrls.results.errors.get_parent().visible = false
|
|
|
|
_ctrls.results.warnings.text = "0"
|
|
_ctrls.results.warnings.get_parent().visible = false
|
|
|
|
_ctrls.results.orphans.text = "0"
|
|
_ctrls.results.orphans.get_parent().visible = false
|
|
|
|
|
|
func load_result_json():
|
|
var summary = get_file_as_text(GutEditorGlobals.editor_run_json_results_path)
|
|
var test_json_conv = JSON.new()
|
|
if (test_json_conv.parse(summary) != OK):
|
|
return
|
|
var results = test_json_conv.get_data()
|
|
|
|
results_tree.load_json_results(results)
|
|
|
|
var summary_json = results['test_scripts']['props']
|
|
_ctrls.results.passing.text = str(int(summary_json.passing))
|
|
_ctrls.results.passing.get_parent().visible = true
|
|
|
|
_ctrls.results.failing.text = str(int(summary_json.failures))
|
|
_ctrls.results.failing.get_parent().visible = true
|
|
|
|
_ctrls.results.pending.text = str(int(summary_json.pending) + int(summary_json.risky))
|
|
_ctrls.results.pending.get_parent().visible = _ctrls.results.pending.text != '0'
|
|
|
|
_ctrls.results.errors.text = str(int(summary_json.errors))
|
|
_ctrls.results.errors.get_parent().visible = _ctrls.results.errors.text != '0'
|
|
|
|
_ctrls.results.warnings.text = str(int(summary_json.warnings))
|
|
_ctrls.results.warnings.get_parent().visible = _ctrls.results.warnings.text != '0'
|
|
|
|
_ctrls.results.orphans.text = str(int(summary_json.orphans))
|
|
_ctrls.results.orphans.get_parent().visible = _ctrls.results.orphans.text != '0' and !_gut_config.options.hide_orphans
|
|
|
|
if(summary_json.tests == 0):
|
|
_light_color = Color(1, 0, 0, .75)
|
|
elif(summary_json.failures != 0):
|
|
_light_color = Color(1, 0, 0, .75)
|
|
elif(summary_json.pending != 0 or summary_json.risky != 0):
|
|
_light_color = Color(1, 1, 0, .75)
|
|
else:
|
|
_light_color = Color(0, 1, 0, .75)
|
|
|
|
_ctrls.light.visible = true
|
|
|
|
|
|
func load_result_text():
|
|
results_text.load_file(GutEditorGlobals.editor_run_bbcode_results_path)
|
|
|
|
|
|
func load_result_output():
|
|
load_result_text()
|
|
load_result_json()
|
|
|
|
|
|
func set_interface(value):
|
|
_interface = value
|
|
results_tree.set_interface(_interface)
|
|
|
|
|
|
func set_plugin(value):
|
|
_gut_plugin = value
|
|
|
|
|
|
func set_panel_button(value):
|
|
_panel_button = value
|
|
|
|
|
|
func write_file(path, content):
|
|
var f = FileAccess.open(path, FileAccess.WRITE)
|
|
if(f != null):
|
|
f.store_string(content)
|
|
f = null;
|
|
|
|
return FileAccess.get_open_error()
|
|
|
|
|
|
func get_file_as_text(path):
|
|
var to_return = ''
|
|
var f = FileAccess.open(path, FileAccess.READ)
|
|
if(f != null):
|
|
to_return = f.get_as_text()
|
|
f = null
|
|
return to_return
|
|
|
|
|
|
func get_text_output_control():
|
|
return results_text
|
|
|
|
|
|
func add_output_text(text):
|
|
results_text.add_text(text)
|
|
|
|
|
|
func show_about():
|
|
var about = AboutWindow.instantiate()
|
|
add_child(about)
|
|
about.popup_centered()
|
|
about.confirmed.connect(about.queue_free)
|
|
|
|
|
|
func show_me():
|
|
if(owner is Window):
|
|
owner.grab_focus()
|
|
else:
|
|
_gut_plugin.make_bottom_panel_item_visible(self)
|
|
|
|
|
|
func show_hide():
|
|
if(owner is Window):
|
|
if(owner.has_focus()):
|
|
var win_to_focus_on = EditorInterface.get_editor_main_screen().get_parent()
|
|
while(win_to_focus_on != null and win_to_focus_on is not Window):
|
|
win_to_focus_on = win_to_focus_on.get_parent()
|
|
if(win_to_focus_on != null):
|
|
win_to_focus_on.grab_focus()
|
|
else:
|
|
owner.grab_focus()
|
|
else:
|
|
pass
|
|
# We don't have to do anything when we are docked because the GUT
|
|
# bottom panel has the shortcut and it does the toggling all on its
|
|
# own.
|
|
|
|
|
|
func get_shortcut_dialog():
|
|
return _ctrls.shortcut_dialog
|
|
|
|
|
|
func results_vert_layout():
|
|
if(results_tree.get_parent() != results_v_split):
|
|
results_tree.reparent(results_v_split)
|
|
results_text.reparent(results_v_split)
|
|
results_v_split.visible = true
|
|
results_h_split.visible = false
|
|
|
|
|
|
func results_horiz_layout():
|
|
if(results_tree.get_parent() != results_h_split):
|
|
results_tree.reparent(results_h_split)
|
|
results_text.reparent(results_h_split)
|
|
results_v_split.visible = false
|
|
results_h_split.visible = true
|