test runner added
This commit is contained in:
@@ -29,7 +29,10 @@
|
||||
"Bash(then wc -l /home/ckoch/Documents/Development/FFCardGame/data/scan_errors.log)",
|
||||
"Bash(else echo \"No errors logged\")",
|
||||
"Bash(fi)",
|
||||
"Bash(git -C /home/ckoch/Documents/Development/FFCardGame log --oneline -20)"
|
||||
"Bash(git -C /home/ckoch/Documents/Development/FFCardGame log --oneline -20)",
|
||||
"Bash(git clone:*)",
|
||||
"Bash(godot --headless --script res://addons/gut/gut_cmdln.gd -gdir=res://tests -gexit:*)",
|
||||
"Bash(/home/ckoch/Downloads/Godot_v4.2-stable_linux.x86_64:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -30,14 +30,14 @@ dock_5="Inspector,Node,History"
|
||||
open_scenes=PackedStringArray("res://scenes/main.tscn")
|
||||
current_scene="res://scenes/main.tscn"
|
||||
center_split_offset=-491
|
||||
selected_default_debugger_tab_idx=0
|
||||
selected_default_debugger_tab_idx=1
|
||||
selected_main_editor_idx=2
|
||||
selected_bottom_panel_item=0
|
||||
selected_bottom_panel_item=1
|
||||
|
||||
[ScriptEditor]
|
||||
|
||||
open_scripts=["res://scripts/ui/ActionLog.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/game/CardInstance.gd", "res://scripts/visual/CardVisual.gd", "res://scripts/game/CPPool.gd", "res://scripts/ui/DamageDisplay.gd", "res://scripts/GameController.gd", "res://scripts/game/GameState.gd", "res://scripts/ui/GameUI.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/Main.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/game/Player.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/visual/TableCamera.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/game/UndoSystem.gd"]
|
||||
selected_script="res://scripts/game/CardInstance.gd"
|
||||
open_scripts=["res://scripts/ui/ActionLog.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/game/CardInstance.gd", "res://scripts/visual/CardVisual.gd", "res://scripts/game/CPPool.gd", "res://scripts/ui/DamageDisplay.gd", "res://scripts/GameController.gd", "res://scripts/game/GameState.gd", "res://scripts/ui/GameUI.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/Main.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/game/Player.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/visual/TableCamera.gd", "res://scripts/visual/TableSetup.gd", "res://tests/fixtures/test_card_data.gd", "res://tests/integration/test_game_state.gd", "res://tests/unit/test_zone.gd", "res://scripts/game/UndoSystem.gd", "res://scripts/game/Zone.gd"]
|
||||
selected_script="res://scripts/game/Zone.gd"
|
||||
open_help=[]
|
||||
script_split_offset=140
|
||||
list_split_offset=0
|
||||
@@ -52,7 +52,7 @@ selected_shader=""
|
||||
|
||||
log_filter_0=true
|
||||
log_filter_2=true
|
||||
log_filter_1=false
|
||||
log_filter_1=true
|
||||
log_filter_3=true
|
||||
log_filter_4=true
|
||||
collapse=false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ea4bc82a6ad023ab7ee23ee620429895
|
||||
::res://::1769555243
|
||||
::res://::1769613134
|
||||
background_1.png::CompressedTexture2D::259206091835802070::1769464008::1769464212::1::::<><>::
|
||||
card_back.png::CompressedTexture2D::4833498016096001590::1769466370::1769466517::1::::<><>::
|
||||
FF14_Playmat__12516.webp::CompressedTexture2D::1641665221299209414::1769277769::1769280957::1::::<><>::
|
||||
@@ -8,6 +8,115 @@ JimNightshade-Regular.ttf::FontFile::7644275900508645331::1757609064::1769555265
|
||||
README.md::TextFile::-1::1769279531::0::1::::<><>::
|
||||
Screenshot 2026-01-24 at 12-53-03 Untitled-3 - fftcgrulesheet-en.pdf.png::CompressedTexture2D::5958662832102035034::1769277183::1769280957::1::::<><>::
|
||||
title_menu.png::CompressedTexture2D::4103292590061137586::1769543314::1769543405::1::::<><>::
|
||||
::res://addons/::1769611855
|
||||
::res://addons/gut/::1769611855
|
||||
autofree.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
awaiter.gd::GDScript::-1::1769611855::0::1::::<>Node<>::
|
||||
collected_script.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
collected_test.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
comparator.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
compare_result.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
diff_formatter.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
diff_tool.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
doubler.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
double_tools.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
gut.gd::GDScript::-1::1769611855::0::1::::<>Node<>::
|
||||
GutScene.gd::GDScript::-1::1769611855::0::1::::<>Node2D<>::
|
||||
GutScene.tscn::PackedScene::900020720033095020::1769611855::0::1::::<><>::res://addons/gut/GutScene.gd<>uid://duxblir3vu8x7::::res://addons/gut/gui/NormalGui.tscn<>uid://cnqqdfsn80ise::::res://addons/gut/gui/MinGui.tscn
|
||||
gut_cmdln.gd::GDScript::-1::1769611855::0::1::::<>SceneTree<>::
|
||||
gut_config.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
gut_plugin.gd::GDScript::-1::1769611855::0::1::::<>EditorPlugin<>::
|
||||
gut_to_move.gd::GDScript::-1::1769611855::0::1::::<>Node<>::
|
||||
gut_vscode_debugger.gd::GDScript::-1::1769611855::0::1::::<>SceneTree<>::
|
||||
hook_script.gd::GDScript::-1::1769611855::0::1::::GutHookScript<>RefCounted<>::
|
||||
icon.png::CompressedTexture2D::3890841352431263106::1769611855::1769611855::1::::<><>::
|
||||
inner_class_registry.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
input_factory.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
input_sender.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
junit_xml_export.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
LICENSE.md::TextFile::-1::1769611855::0::1::::<><>::
|
||||
logger.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
method_maker.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
one_to_many.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
optparse.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
orphan_counter.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
parameter_factory.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
parameter_handler.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
plugin.cfg::TextFile::-1::1769611855::0::1::::<><>::
|
||||
printers.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
result_exporter.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
script_parser.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
signal_watcher.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
source_code_pro.fnt::FontFile::5125156706837925311::1769611855::1769611855::1::::<><>::
|
||||
spy.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
strutils.gd::GDScript::-1::1769611855::0::1::::GutStringUtils<>RefCounted<>::
|
||||
stubber.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
stub_params.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
summary.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
test.gd::GDScript::-1::1769611855::0::1::::GutTest<>Node<>::
|
||||
test_collector.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
thing_counter.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
UserFileViewer.gd::GDScript::-1::1769611855::0::1::::<>Window<>::
|
||||
UserFileViewer.tscn::PackedScene::3676575661945431583::1769611855::0::1::::<><>::res://addons/gut/UserFileViewer.gd
|
||||
utils.gd::GDScript::-1::1769611855::0::1::::GutUtils<>Object<>::
|
||||
version_conversion.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
::res://addons/gut/double_templates/::1769611855
|
||||
function_template.txt::TextFile::-1::1769611855::0::1::::<><>::
|
||||
init_template.txt::TextFile::-1::1769611855::0::1::::<><>::
|
||||
script_template.txt::TextFile::-1::1769611855::0::1::::<><>::
|
||||
::res://addons/gut/fonts/::1769611855
|
||||
AnonymousPro-Bold.ttf::FontFile::7090493737101227053::1769611855::1769611855::1::::<><>::
|
||||
AnonymousPro-BoldItalic.ttf::FontFile::761724172175864::1769611855::1769611855::1::::<><>::
|
||||
AnonymousPro-Italic.ttf::FontFile::503495830806397342::1769611855::1769611855::1::::<><>::
|
||||
AnonymousPro-Regular.ttf::FontFile::6954778653853521719::1769611855::1769611855::1::::<><>::
|
||||
CourierPrime-Bold.ttf::FontFile::2896714378692211288::1769611855::1769611855::1::::<><>::
|
||||
CourierPrime-BoldItalic.ttf::FontFile::977220797652290018::1769611855::1769611855::1::::<><>::
|
||||
CourierPrime-Italic.ttf::FontFile::846855295635301556::1769611855::1769611855::1::::<><>::
|
||||
CourierPrime-Regular.ttf::FontFile::3314864826498090510::1769611855::1769611855::1::::<><>::
|
||||
LobsterTwo-Bold.ttf::FontFile::5632861098580159618::1769611855::1769611855::1::::<><>::
|
||||
LobsterTwo-BoldItalic.ttf::FontFile::3182965850633207044::1769611855::1769611855::1::::<><>::
|
||||
LobsterTwo-Italic.ttf::FontFile::7759867820021968437::1769611855::1769611855::1::::<><>::
|
||||
LobsterTwo-Regular.ttf::FontFile::2115948870915788183::1769611855::1769611855::1::::<><>::
|
||||
OFL.txt::TextFile::-1::1769611855::0::1::::<><>::
|
||||
::res://addons/gut/gui/::1769611855
|
||||
arrow.png::CompressedTexture2D::2222304255268796957::1769611855::1769611855::1::::<><>::
|
||||
BottomPanelShortcuts.gd::GDScript::-1::1769611855::0::1::::<>Window<>::
|
||||
BottomPanelShortcuts.tscn::PackedScene::3672212108687301338::1769611855::0::1::::<><>::uid://sfb1fw8j6ufu::::res://addons/gut/gui/ShortcutButton.tscn<>res://addons/gut/gui/BottomPanelShortcuts.gd
|
||||
editor_globals.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
GutBottomPanel.gd::GDScript::-1::1769611855::0::1::::<>Control<>::
|
||||
GutBottomPanel.tscn::PackedScene::4354655867367614688::1769611855::0::1::::<><>::res://addons/gut/gui/GutBottomPanel.gd<>uid://bsk32dh41b4gs::::res://addons/gut/gui/BottomPanelShortcuts.tscn<>uid://0yunjxtaa8iw::::res://addons/gut/gui/RunAtCursor.tscn<>uid://cr6tvdv0ve6cv::::res://addons/gut/gui/play.png<>uid://4gyyn12um08h::::res://addons/gut/gui/RunResults.tscn<>uid://bqmo4dj64c7yl::::res://addons/gut/gui/OutputText.tscn
|
||||
GutControl.gd::GDScript::-1::1769611855::0::1::::<>Control<>::
|
||||
GutControl.tscn::PackedScene::2054171664569605320::1769611855::0::1::::<><>::res://addons/gut/gui/GutControl.gd
|
||||
GutRunner.gd::GDScript::-1::1769611855::0::1::::<>Node2D<>::
|
||||
GutRunner.tscn::PackedScene::3560702166169920196::1769611855::0::1::::<><>::res://addons/gut/gui/GutRunner.gd<>uid://m28heqtswbuq::::res://addons/gut/GutScene.tscn
|
||||
GutSceneTheme.tres::Theme::6076084572326576944::1769611855::0::1::::<><>::uid://c6c7gnx36opr0::::res://addons/gut/fonts/AnonymousPro-Regular.ttf
|
||||
gut_config_gui.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
gut_gui.gd::GDScript::-1::1769611855::0::1::::<>Control<>::
|
||||
gut_user_preferences.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
MinGui.tscn::PackedScene::5719303499070539904::1769611855::0::1::::<><>::uid://cstkhwkpajvqu::::res://addons/gut/gui/GutSceneTheme.tres<>uid://bnh0lslf4yh87::::res://addons/gut/fonts/CourierPrime-Regular.ttf<>res://addons/gut/gui/gut_gui.gd<>uid://bvrqqgjpyouse::::res://addons/gut/gui/ResizeHandle.tscn
|
||||
NormalGui.tscn::PackedScene::8610600420658602210::1769611855::0::1::::<><>::uid://cstkhwkpajvqu::::res://addons/gut/gui/GutSceneTheme.tres<>res://addons/gut/gui/gut_gui.gd<>uid://bnh0lslf4yh87::::res://addons/gut/fonts/CourierPrime-Regular.ttf<>uid://bvrqqgjpyouse::::res://addons/gut/gui/ResizeHandle.tscn
|
||||
OutputText.gd::GDScript::-1::1769611855::0::1::::<>VBoxContainer<>::
|
||||
OutputText.tscn::PackedScene::3535116713792967483::1769611855::0::1::::<><>::res://addons/gut/gui/OutputText.gd
|
||||
panel_controls.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
play.png::CompressedTexture2D::6031238735318256293::1769611855::1769611855::1::::<><>::
|
||||
ResizeHandle.gd::GDScript::-1::1769611855::0::1::::<>ColorRect<>::
|
||||
ResizeHandle.tscn::PackedScene::3896481196255057096::1769611855::0::1::::<><>::res://addons/gut/gui/ResizeHandle.gd
|
||||
ResultsTree.gd::GDScript::-1::1769611855::0::1::::<>Control<>::
|
||||
ResultsTree.tscn::PackedScene::7970351583819098362::1769611855::0::1::::<><>::res://addons/gut/gui/ResultsTree.gd
|
||||
RunAtCursor.gd::GDScript::-1::1769611855::0::1::::<>Control<>::
|
||||
RunAtCursor.tscn::PackedScene::1805504221328301514::1769611855::0::1::::<><>::res://addons/gut/gui/RunAtCursor.gd<>uid://cr6tvdv0ve6cv::::res://addons/gut/gui/play.png<>uid://6wra5rxmfsrl::::res://addons/gut/gui/arrow.png
|
||||
RunResults.gd::GDScript::-1::1769611855::0::1::::<>Control<>::
|
||||
RunResults.tscn::PackedScene::2049363523129861965::1769611855::0::1::::<><>::res://addons/gut/gui/RunResults.gd<>uid://dls5r5f6157nq::::res://addons/gut/gui/ResultsTree.tscn
|
||||
script_text_editor_controls.gd::GDScript::-1::1769611855::0::1::::<>RefCounted<>::
|
||||
Settings.tscn::PackedScene::6291468949329504810::1769611855::0::1::::<><>::
|
||||
ShortcutButton.gd::GDScript::-1::1769611855::0::1::::<>Control<>::
|
||||
ShortcutButton.tscn::PackedScene::1273828520004261174::1769611855::0::1::::<><>::res://addons/gut/gui/ShortcutButton.gd
|
||||
::res://addons/gut/images/::1769611855
|
||||
Folder.svg::CompressedTexture2D::7694651741458501535::1769611855::1769611855::1::::<><>::
|
||||
green.png::CompressedTexture2D::3896279560051998512::1769611855::1769611855::1::::<><>::
|
||||
red.png::CompressedTexture2D::2443263267725858415::1769611855::1769611855::1::::<><>::
|
||||
Script.svg::CompressedTexture2D::4817060143842260657::1769611855::1769611855::1::::<><>::
|
||||
yellow.png::CompressedTexture2D::1043265824178416429::1769611855::1769611855::1::::<><>::
|
||||
::res://assets/::1769279430
|
||||
::res://assets/cards/::1769466517
|
||||
1-003C_eg.jpg::CompressedTexture2D::3078340571116611252::1769279471::1769280956::1::::<><>::
|
||||
@@ -28,16 +137,16 @@ scan_errors.log::TextFile::-1::1769539203::0::1::::<><>::
|
||||
::res://docs/::1769279608
|
||||
CARD_FORMAT.md::TextFile::-1::1769279608::0::1::::<><>::
|
||||
DESIGN.md::TextFile::-1::1769279572::0::1::::<><>::
|
||||
::res://scenes/::1769555163
|
||||
::res://scenes/::1769558936
|
||||
game_controller.tscn::PackedScene::3882700613993784342::1769285267::0::1::::<><>::res://scripts/GameController.gd
|
||||
main.tscn::PackedScene::5942992277112036945::1769555163::0::1::::<><>::res://scripts/Main.gd
|
||||
main.tscn::PackedScene::5942992277112036945::1769558936::0::1::::<><>::res://scripts/Main.gd
|
||||
::res://scenes/card/::1769279430
|
||||
::res://scenes/main/::1769279430
|
||||
::res://scenes/table/::1769279430
|
||||
::res://scenes/ui/::1769279430
|
||||
::res://scripts/::1769554938
|
||||
GameController.gd::GDScript::-1::1769542879::0::1::::<>Node<>::
|
||||
Main.gd::GDScript::-1::1769473058::0::1::::<>Node3D<>::
|
||||
::res://scripts/::1769558794
|
||||
GameController.gd::GDScript::-1::1769557430::0::1::::<>Node<>::
|
||||
Main.gd::GDScript::-1::1769558794::0::1::::<>Node3D<>::
|
||||
::res://scripts/autoload/::1769308378
|
||||
CardDatabase.gd::GDScript::-1::1769308329::0::1::::<>Node<>::
|
||||
GameManager.gd::GDScript::-1::1769308378::0::1::::<>Node<>::
|
||||
@@ -50,12 +159,12 @@ Player.gd::GDScript::-1::1769302256::0::1::::Player<>RefCounted<>::
|
||||
TurnManager.gd::GDScript::-1::1769302284::0::1::::TurnManager<>RefCounted<>::
|
||||
UndoSystem.gd::GDScript::-1::1769301595::0::1::::UndoSystem<>RefCounted<>::
|
||||
Zone.gd::GDScript::-1::1769302225::0::1::::Zone<>RefCounted<>::
|
||||
::res://scripts/ui/::1769555036
|
||||
::res://scripts/ui/::1769558772
|
||||
ActionLog.gd::GDScript::-1::1769298563::0::1::::ActionLog<>Control<>::
|
||||
DamageDisplay.gd::GDScript::-1::1769280183::0::1::::DamageDisplay<>Control<>::
|
||||
GameUI.gd::GDScript::-1::1769472787::0::1::::GameUI<>CanvasLayer<>::
|
||||
HandDisplay.gd::GDScript::-1::1769381830::0::1::::HandDisplay<>Control<>::
|
||||
MainMenu.gd::GDScript::-1::1769555036::0::1::::MainMenu<>CanvasLayer<>::
|
||||
HandDisplay.gd::GDScript::-1::1769558772::0::1::::HandDisplay<>Control<>::
|
||||
MainMenu.gd::GDScript::-1::1769557191::0::1::::MainMenu<>CanvasLayer<>::
|
||||
PauseMenu.gd::GDScript::-1::1769287615::0::1::::PauseMenu<>CanvasLayer<>::
|
||||
::res://scripts/visual/::1769471729
|
||||
CardVisual.gd::GDScript::-1::1769460118::0::1::::CardVisual<>Node3D<>::
|
||||
@@ -4080,4 +4189,15 @@ Re-197C-13-125R.jpg::CompressedTexture2D::6616505751106391433::1769306351::17693
|
||||
Re-198H-13-127H.jpg::CompressedTexture2D::5413433616482804995::1769306351::1769308611::1::::<><>::
|
||||
Re-199H-19-125H.jpg::CompressedTexture2D::4308488034767344289::1769306351::1769308619::1::::<><>::
|
||||
Re-200L-19-128L.jpg::CompressedTexture2D::8097057901487536182::1769306356::1769308614::1::::<><>::
|
||||
::res://tests/::1769610959
|
||||
::res://tests/fixtures/::1769610993
|
||||
test_card_data.gd::GDScript::-1::1769610993::0::1::::TestCardData<>RefCounted<>::
|
||||
::res://tests/integration/::1769611345
|
||||
test_game_state.gd::GDScript::-1::1769611345::0::1::::<>GutTest<>::
|
||||
::res://tests/unit/::1769611266
|
||||
test_card_instance.gd::GDScript::-1::1769611194::0::1::::<>GutTest<>::
|
||||
test_cppool.gd::GDScript::-1::1769611095::0::1::::<>GutTest<>::
|
||||
test_player.gd::GDScript::-1::1769611266::0::1::::<>GutTest<>::
|
||||
test_turn_manager.gd::GDScript::-1::1769611145::0::1::::<>GutTest<>::
|
||||
test_zone.gd::GDScript::-1::1769611040::0::1::::<>GutTest<>::
|
||||
::res://tools/::1769541891
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
res://scenes/main.tscn
|
||||
res://scripts/ui/MainMenu.gd
|
||||
|
||||
@@ -10,7 +10,7 @@ run_reload_scripts=true
|
||||
[recent_files]
|
||||
|
||||
scenes=["res://scenes/main.tscn"]
|
||||
scripts=["res://scripts/ui/DamageDisplay.gd", "res://scripts/visual/CardVisual.gd", "res://scripts/GameController.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/GameUI.gd", "res://scripts/game/Player.gd", "res://scripts/game/CardInstance.gd", "res://scripts/game/CPPool.gd", "res://scripts/game/GameState.gd"]
|
||||
scripts=["res://scripts/game/Zone.gd", "res://tests/unit/test_zone.gd", "res://tests/integration/test_game_state.gd", "res://tests/fixtures/test_card_data.gd", "res://scripts/ui/DamageDisplay.gd", "res://scripts/visual/CardVisual.gd", "res://scripts/GameController.gd", "res://scripts/visual/PlaymatRenderer.gd", "res://scripts/ui/MainMenu.gd", "res://scripts/ui/GameUI.gd"]
|
||||
|
||||
[linked_properties]
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
state={
|
||||
"bookmarks": PackedInt32Array(),
|
||||
"breakpoints": PackedInt32Array(),
|
||||
"column": 0,
|
||||
"column": 8,
|
||||
"folded_lines": Array[int]([]),
|
||||
"h_scroll_position": 0,
|
||||
"row": 305,
|
||||
"scroll_position": 331.0,
|
||||
"row": 314,
|
||||
"scroll_position": 309.0,
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
@@ -91,7 +91,7 @@ state={
|
||||
"folded_lines": Array[int]([]),
|
||||
"h_scroll_position": 0,
|
||||
"row": 225,
|
||||
"scroll_position": 226.0,
|
||||
"scroll_position": 0.0,
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
@@ -147,7 +147,7 @@ state={
|
||||
"folded_lines": Array[int]([]),
|
||||
"h_scroll_position": 0,
|
||||
"row": 33,
|
||||
"scroll_position": 33.0,
|
||||
"scroll_position": 0.0,
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
@@ -249,3 +249,59 @@ state={
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
|
||||
[res://tests/fixtures/test_card_data.gd]
|
||||
|
||||
state={
|
||||
"bookmarks": PackedInt32Array(),
|
||||
"breakpoints": PackedInt32Array(),
|
||||
"column": 0,
|
||||
"folded_lines": Array[int]([]),
|
||||
"h_scroll_position": 0,
|
||||
"row": 20,
|
||||
"scroll_position": 12.0,
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
|
||||
[res://tests/integration/test_game_state.gd]
|
||||
|
||||
state={
|
||||
"bookmarks": PackedInt32Array(),
|
||||
"breakpoints": PackedInt32Array(),
|
||||
"column": 0,
|
||||
"folded_lines": Array[int]([]),
|
||||
"h_scroll_position": 0,
|
||||
"row": 101,
|
||||
"scroll_position": 93.0,
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
|
||||
[res://tests/unit/test_zone.gd]
|
||||
|
||||
state={
|
||||
"bookmarks": PackedInt32Array(),
|
||||
"breakpoints": PackedInt32Array(),
|
||||
"column": 0,
|
||||
"folded_lines": Array[int]([]),
|
||||
"h_scroll_position": 0,
|
||||
"row": 318,
|
||||
"scroll_position": 310.0,
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
|
||||
[res://scripts/game/Zone.gd]
|
||||
|
||||
state={
|
||||
"bookmarks": PackedInt32Array(),
|
||||
"breakpoints": PackedInt32Array(),
|
||||
"column": 0,
|
||||
"folded_lines": Array[int]([]),
|
||||
"h_scroll_position": 0,
|
||||
"row": 22,
|
||||
"scroll_position": 22.0,
|
||||
"selection": false,
|
||||
"syntax_highlighter": "GDScript"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -47,6 +47,42 @@ list=Array[Dictionary]([{
|
||||
"language": &"GDScript",
|
||||
"path": "res://scripts/ui/GameUI.gd"
|
||||
}, {
|
||||
"base": &"Logger",
|
||||
"class": &"GutErrorTracker",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
"path": "res://addons/gut/error_tracker.gd"
|
||||
}, {
|
||||
"base": &"RefCounted",
|
||||
"class": &"GutHookScript",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
"path": "res://addons/gut/hook_script.gd"
|
||||
}, {
|
||||
"base": &"RefCounted",
|
||||
"class": &"GutStringUtils",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
"path": "res://addons/gut/strutils.gd"
|
||||
}, {
|
||||
"base": &"Node",
|
||||
"class": &"GutTest",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
"path": "res://addons/gut/test.gd"
|
||||
}, {
|
||||
"base": &"RefCounted",
|
||||
"class": &"GutTrackedError",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
"path": "res://addons/gut/gut_tracked_error.gd"
|
||||
}, {
|
||||
"base": &"Object",
|
||||
"class": &"GutUtils",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
"path": "res://addons/gut/utils.gd"
|
||||
}, {
|
||||
"base": &"Control",
|
||||
"class": &"HandDisplay",
|
||||
"icon": "",
|
||||
@@ -90,6 +126,12 @@ list=Array[Dictionary]([{
|
||||
"path": "res://scripts/visual/TableSetup.gd"
|
||||
}, {
|
||||
"base": &"RefCounted",
|
||||
"class": &"TestCardData",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
"path": "res://tests/fixtures/test_card_data.gd"
|
||||
}, {
|
||||
"base": &"RefCounted",
|
||||
"class": &"TurnManager",
|
||||
"icon": "",
|
||||
"language": &"GDScript",
|
||||
|
||||
Binary file not shown.
9
.gutconfig.json
Normal file
9
.gutconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"dirs": ["res://tests/unit", "res://tests/integration"],
|
||||
"prefix": "test_",
|
||||
"suffix": ".gd",
|
||||
"include_subdirs": true,
|
||||
"log_level": 1,
|
||||
"should_exit": false,
|
||||
"font_size": 16
|
||||
}
|
||||
127
addons/gut/GutScene.gd
Normal file
127
addons/gut/GutScene.gd
Normal file
@@ -0,0 +1,127 @@
|
||||
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
|
||||
|
||||
var gut = null :
|
||||
set(val):
|
||||
gut = val
|
||||
_set_gut(val)
|
||||
|
||||
|
||||
func _ready():
|
||||
_normal_gui.switch_modes.connect(use_compact_mode.bind(true))
|
||||
_compact_gui.switch_modes.connect(use_compact_mode.bind(false))
|
||||
|
||||
_normal_gui.set_title("GUT")
|
||||
_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 _test_running_setup():
|
||||
set_font_size(100)
|
||||
_normal_gui.get_textbox().text = "hello world, how are you doing?"
|
||||
|
||||
# ------------------------
|
||||
# Private
|
||||
# ------------------------
|
||||
func _set_gut(val):
|
||||
if(_normal_gui.get_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():
|
||||
return _normal_gui.get_textbox()
|
||||
|
||||
|
||||
func set_font_size(new_size):
|
||||
var rtl = _normal_gui.get_textbox()
|
||||
|
||||
rtl.set('theme_override_font_sizes/bold_italics_font_size', new_size)
|
||||
rtl.set('theme_override_font_sizes/bold_font_size', new_size)
|
||||
rtl.set('theme_override_font_sizes/italics_font_size', new_size)
|
||||
rtl.set('theme_override_font_sizes/normal_font_size', new_size)
|
||||
|
||||
|
||||
func set_font(font_name):
|
||||
_set_all_fonts_in_rtl(_normal_gui.get_textbox(), font_name)
|
||||
|
||||
|
||||
func _set_font(rtl, font_name, custom_name):
|
||||
if(font_name == null):
|
||||
rtl.remove_theme_font_override(custom_name)
|
||||
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_in_rtl(rtl, base_name):
|
||||
if(base_name == 'Default'):
|
||||
_set_font(rtl, null, 'normal_font')
|
||||
_set_font(rtl, null, 'bold_font')
|
||||
_set_font(rtl, null, 'italics_font')
|
||||
_set_font(rtl, null, 'bold_italics_font')
|
||||
else:
|
||||
_set_font(rtl, base_name + '-Regular', 'normal_font')
|
||||
_set_font(rtl, base_name + '-Bold', 'bold_font')
|
||||
_set_font(rtl, base_name + '-Italic', 'italics_font')
|
||||
_set_font(rtl, base_name + '-BoldItalic', 'bold_italics_font')
|
||||
|
||||
|
||||
func set_default_font_color(color):
|
||||
_normal_gui.get_textbox().set('custom_colors/default_color', color)
|
||||
|
||||
|
||||
func set_background_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
|
||||
16
addons/gut/GutScene.tscn
Normal file
16
addons/gut/GutScene.tscn
Normal file
@@ -0,0 +1,16 @@
|
||||
[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="PackedScene" uid="uid://duxblir3vu8x7" path="res://addons/gut/gui/NormalGui.tscn" id="2_j6ywb"]
|
||||
[ext_resource type="PackedScene" uid="uid://cnqqdfsn80ise" path="res://addons/gut/gui/MinGui.tscn" id="3_3glw1"]
|
||||
|
||||
[node name="GutScene" type="Node2D"]
|
||||
script = ExtResource("1_b4m8y")
|
||||
|
||||
[node name="Normal" parent="." instance=ExtResource("2_j6ywb")]
|
||||
|
||||
[node name="Compact" parent="." instance=ExtResource("3_3glw1")]
|
||||
offset_left = 5.0
|
||||
offset_top = 273.0
|
||||
offset_right = 265.0
|
||||
offset_bottom = 403.0
|
||||
22
addons/gut/LICENSE.md
Normal file
22
addons/gut/LICENSE.md
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright (c) 2018 Tom "Butch" Wesley
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
52
addons/gut/UserFileViewer.gd
Normal file
52
addons/gut/UserFileViewer.gd
Normal file
@@ -0,0 +1,52 @@
|
||||
extends Window
|
||||
|
||||
@onready var rtl = $TextDisplay/RichTextLabel
|
||||
|
||||
func _get_file_as_text(path):
|
||||
var to_return = null
|
||||
var f = FileAccess.open(path, FileAccess.READ)
|
||||
if(f != null):
|
||||
to_return = f.get_as_text()
|
||||
else:
|
||||
to_return = str('ERROR: Could not open file. Error code ', FileAccess.get_open_error())
|
||||
return to_return
|
||||
|
||||
func _ready():
|
||||
rtl.clear()
|
||||
|
||||
func _on_OpenFile_pressed():
|
||||
$FileDialog.popup_centered()
|
||||
|
||||
func _on_FileDialog_file_selected(path):
|
||||
show_file(path)
|
||||
|
||||
func _on_Close_pressed():
|
||||
self.hide()
|
||||
|
||||
func show_file(path):
|
||||
var text = _get_file_as_text(path)
|
||||
if(text == ''):
|
||||
text = '<Empty File>'
|
||||
rtl.set_text(text)
|
||||
self.window_title = path
|
||||
|
||||
func show_open():
|
||||
self.popup_centered()
|
||||
$FileDialog.popup_centered()
|
||||
|
||||
func get_rich_text_label():
|
||||
return $TextDisplay/RichTextLabel
|
||||
|
||||
func _on_Home_pressed():
|
||||
rtl.scroll_to_line(0)
|
||||
|
||||
func _on_End_pressed():
|
||||
rtl.scroll_to_line(rtl.get_line_count() -1)
|
||||
|
||||
func _on_Copy_pressed():
|
||||
return
|
||||
# OS.clipboard = rtl.text
|
||||
|
||||
func _on_file_dialog_visibility_changed():
|
||||
if rtl.text.length() == 0 and not $FileDialog.visible:
|
||||
self.hide()
|
||||
92
addons/gut/UserFileViewer.tscn
Normal file
92
addons/gut/UserFileViewer.tscn
Normal file
@@ -0,0 +1,92 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bsm7wtt1gie4v"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gut/UserFileViewer.gd" id="1"]
|
||||
|
||||
[node name="UserFileViewer" type="Window"]
|
||||
exclusive = true
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="."]
|
||||
access = 1
|
||||
show_hidden_files = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="TextDisplay" type="ColorRect" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_right = -10.0
|
||||
offset_bottom = -65.0
|
||||
color = Color(0.2, 0.188235, 0.188235, 1)
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="TextDisplay"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
focus_mode = 2
|
||||
text = "In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used before final copy is available, but it may also be used to temporarily replace copy in a process called greeking, which allows designers to consider form without the meaning of the text influencing the design.
|
||||
|
||||
Lorem ipsum is typically a corrupted version of De finibus bonorum et malorum, a first-century BCE text by the Roman statesman and philosopher Cicero, with words altered, added, and removed to make it nonsensical, improper Latin.
|
||||
|
||||
Versions of the Lorem ipsum text have been used in typesetting at least since the 1960s, when it was popularized by advertisements for Letraset transfer sheets. Lorem ipsum was introduced to the digital world in the mid-1980s when Aldus employed it in graphic and word-processing templates for its desktop publishing program PageMaker. Other popular word processors including Pages and Microsoft Word have since adopted Lorem ipsum as well."
|
||||
selection_enabled = true
|
||||
|
||||
[node name="OpenFile" type="Button" parent="."]
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -158.0
|
||||
offset_top = -50.0
|
||||
offset_right = -84.0
|
||||
offset_bottom = -30.0
|
||||
text = "Open File"
|
||||
|
||||
[node name="Home" type="Button" parent="."]
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -478.0
|
||||
offset_top = -50.0
|
||||
offset_right = -404.0
|
||||
offset_bottom = -30.0
|
||||
text = "Home"
|
||||
|
||||
[node name="Copy" type="Button" parent="."]
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 160.0
|
||||
offset_top = -50.0
|
||||
offset_right = 234.0
|
||||
offset_bottom = -30.0
|
||||
text = "Copy"
|
||||
|
||||
[node name="End" type="Button" parent="."]
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -318.0
|
||||
offset_top = -50.0
|
||||
offset_right = -244.0
|
||||
offset_bottom = -30.0
|
||||
text = "End"
|
||||
|
||||
[node name="Close" type="Button" parent="."]
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 10.0
|
||||
offset_top = -50.0
|
||||
offset_right = 80.0
|
||||
offset_bottom = -30.0
|
||||
text = "Close"
|
||||
|
||||
[connection signal="file_selected" from="FileDialog" to="." method="_on_FileDialog_file_selected"]
|
||||
[connection signal="visibility_changed" from="FileDialog" to="." method="_on_file_dialog_visibility_changed"]
|
||||
[connection signal="pressed" from="OpenFile" to="." method="_on_OpenFile_pressed"]
|
||||
[connection signal="pressed" from="Home" to="." method="_on_Home_pressed"]
|
||||
[connection signal="pressed" from="Copy" to="." method="_on_Copy_pressed"]
|
||||
[connection signal="pressed" from="End" to="." method="_on_End_pressed"]
|
||||
[connection signal="pressed" from="Close" to="." method="_on_Close_pressed"]
|
||||
59
addons/gut/autofree.gd
Normal file
59
addons/gut/autofree.gd
Normal file
@@ -0,0 +1,59 @@
|
||||
# ##############################################################################
|
||||
#(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.
|
||||
#
|
||||
# ##############################################################################
|
||||
# Class used to keep track of objects to be freed and utilities to free them.
|
||||
# ##############################################################################
|
||||
var _to_free = []
|
||||
var _to_queue_free = []
|
||||
|
||||
func add_free(thing):
|
||||
if(typeof(thing) == TYPE_OBJECT):
|
||||
if(!thing is RefCounted):
|
||||
_to_free.append(thing)
|
||||
|
||||
func add_queue_free(thing):
|
||||
_to_queue_free.append(thing)
|
||||
|
||||
func get_queue_free_count():
|
||||
return _to_queue_free.size()
|
||||
|
||||
func get_free_count():
|
||||
return _to_free.size()
|
||||
|
||||
func free_all():
|
||||
for i in range(_to_free.size()):
|
||||
if(is_instance_valid(_to_free[i])):
|
||||
_to_free[i].free()
|
||||
_to_free.clear()
|
||||
|
||||
for i in range(_to_queue_free.size()):
|
||||
if(is_instance_valid(_to_queue_free[i])):
|
||||
_to_queue_free[i].queue_free()
|
||||
_to_queue_free.clear()
|
||||
|
||||
|
||||
70
addons/gut/awaiter.gd
Normal file
70
addons/gut/awaiter.gd
Normal file
@@ -0,0 +1,70 @@
|
||||
extends Node
|
||||
|
||||
signal timeout
|
||||
signal wait_started
|
||||
|
||||
var _wait_time = 0.0
|
||||
var _wait_frames = 0
|
||||
var _signal_to_wait_on = null
|
||||
|
||||
var _elapsed_time = 0.0
|
||||
var _elapsed_frames = 0
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
if(_wait_time != 0.0):
|
||||
_elapsed_time += delta
|
||||
if(_elapsed_time >= _wait_time):
|
||||
_end_wait()
|
||||
|
||||
if(_wait_frames != 0):
|
||||
_elapsed_frames += 1
|
||||
if(_elapsed_frames >= _wait_frames):
|
||||
_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_frames = 0
|
||||
_signal_to_wait_on = null
|
||||
_elapsed_time = 0.0
|
||||
_elapsed_frames = 0
|
||||
timeout.emit()
|
||||
|
||||
|
||||
const ARG_NOT_SET = '_*_argument_*_is_*_not_set_*_'
|
||||
func _signal_callback(
|
||||
arg1=ARG_NOT_SET, arg2=ARG_NOT_SET, arg3=ARG_NOT_SET,
|
||||
arg4=ARG_NOT_SET, arg5=ARG_NOT_SET, arg6=ARG_NOT_SET,
|
||||
arg7=ARG_NOT_SET, arg8=ARG_NOT_SET, arg9=ARG_NOT_SET):
|
||||
|
||||
_signal_to_wait_on.disconnect(_signal_callback)
|
||||
# DO NOT _end_wait here. For other parts of the test to get the signal that
|
||||
# was waited on, we have to wait for a couple more frames. For example, the
|
||||
# signal_watcher doesn't get the signal in time if we don't do this.
|
||||
_wait_frames = 2
|
||||
|
||||
|
||||
func wait_for(x):
|
||||
_wait_time = x
|
||||
wait_started.emit()
|
||||
|
||||
|
||||
func wait_frames(x):
|
||||
_wait_frames = x
|
||||
wait_started.emit()
|
||||
|
||||
|
||||
func wait_for_signal(the_signal, x):
|
||||
the_signal.connect(_signal_callback)
|
||||
_signal_to_wait_on = the_signal
|
||||
_wait_time = x
|
||||
wait_started.emit()
|
||||
|
||||
|
||||
func is_waiting():
|
||||
return _wait_time != 0.0 || _wait_frames != 0
|
||||
|
||||
213
addons/gut/collected_script.gd
Normal file
213
addons/gut/collected_script.gd
Normal file
@@ -0,0 +1,213 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
115
addons/gut/collected_test.gd
Normal file
115
addons/gut/collected_test.gd
Normal file
@@ -0,0 +1,115 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
|
||||
123
addons/gut/comparator.gd
Normal file
123
addons/gut/comparator.gd
Normal file
@@ -0,0 +1,123 @@
|
||||
var _utils = load('res://addons/gut/utils.gd').get_instance()
|
||||
var _strutils = _utils.Strutils.new()
|
||||
var _max_length = 100
|
||||
var _should_compare_int_to_float = true
|
||||
|
||||
const MISSING = '|__missing__gut__compare__value__|'
|
||||
|
||||
func _cannot_compare_text(v1, v2):
|
||||
return str('Cannot compare ', _strutils.types[typeof(v1)], ' with ',
|
||||
_strutils.types[typeof(v2)], '.')
|
||||
|
||||
func _make_missing_string(text):
|
||||
return '<missing ' + text + '>'
|
||||
|
||||
func _create_missing_result(v1, v2, text):
|
||||
var to_return = null
|
||||
var v1_str = format_value(v1)
|
||||
var v2_str = format_value(v2)
|
||||
|
||||
if(typeof(v1) == TYPE_STRING and v1 == MISSING):
|
||||
v1_str = _make_missing_string(text)
|
||||
to_return = _utils.CompareResult.new()
|
||||
elif(typeof(v2) == TYPE_STRING and v2 == MISSING):
|
||||
v2_str = _make_missing_string(text)
|
||||
to_return = _utils.CompareResult.new()
|
||||
|
||||
if(to_return != null):
|
||||
to_return.summary = str(v1_str, ' != ', v2_str)
|
||||
to_return.are_equal = false
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func simple(v1, v2, missing_string=''):
|
||||
var missing_result = _create_missing_result(v1, v2, missing_string)
|
||||
if(missing_result != null):
|
||||
return missing_result
|
||||
|
||||
var result = _utils.CompareResult.new()
|
||||
var cmp_str = null
|
||||
var extra = ''
|
||||
|
||||
var tv1 = typeof(v1)
|
||||
var tv2 = typeof(v2)
|
||||
|
||||
# print(tv1, '::', tv2, ' ', _strutils.types[tv1], '::', _strutils.types[tv2])
|
||||
if(_should_compare_int_to_float and [TYPE_INT, TYPE_FLOAT].has(tv1) and [TYPE_INT, TYPE_FLOAT].has(tv2)):
|
||||
result.are_equal = v1 == v2
|
||||
elif([TYPE_STRING, TYPE_STRING_NAME].has(tv1) and [TYPE_STRING, TYPE_STRING_NAME].has(tv2)):
|
||||
result.are_equal = v1 == v2
|
||||
elif(_utils.are_datatypes_same(v1, v2)):
|
||||
result.are_equal = v1 == v2
|
||||
|
||||
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:
|
||||
cmp_str = '!='
|
||||
result.are_equal = false
|
||||
extra = str('. ', _cannot_compare_text(v1, v2))
|
||||
|
||||
cmp_str = get_compare_symbol(result.are_equal)
|
||||
result.summary = str(format_value(v1), ' ', cmp_str, ' ', format_value(v2), extra)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func shallow(v1, v2):
|
||||
var result = null
|
||||
if(_utils.are_datatypes_same(v1, v2)):
|
||||
if(typeof(v1) in [TYPE_ARRAY, TYPE_DICTIONARY]):
|
||||
result = _utils.DiffTool.new(v1, v2, _utils.DIFF.DEEP)
|
||||
else:
|
||||
result = simple(v1, v2)
|
||||
else:
|
||||
result = simple(v1, v2)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func deep(v1, v2):
|
||||
var result = null
|
||||
|
||||
if(_utils.are_datatypes_same(v1, v2)):
|
||||
if(typeof(v1) in [TYPE_ARRAY, TYPE_DICTIONARY]):
|
||||
result = _utils.DiffTool.new(v1, v2, _utils.DIFF.DEEP)
|
||||
else:
|
||||
result = simple(v1, v2)
|
||||
else:
|
||||
result = simple(v1, v2)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func format_value(val, max_val_length=_max_length):
|
||||
return _strutils.truncate_string(_strutils.type2str(val), max_val_length)
|
||||
|
||||
|
||||
func compare(v1, v2, diff_type=_utils.DIFF.SIMPLE):
|
||||
var result = null
|
||||
if(diff_type == _utils.DIFF.SIMPLE):
|
||||
result = simple(v1, v2)
|
||||
elif(diff_type == _utils.DIFF.DEEP):
|
||||
result = deep(v1, v2)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func get_should_compare_int_to_float():
|
||||
return _should_compare_int_to_float
|
||||
|
||||
|
||||
func set_should_compare_int_to_float(should_compare_int_float):
|
||||
_should_compare_int_to_float = should_compare_int_float
|
||||
|
||||
|
||||
func get_compare_symbol(is_equal):
|
||||
if(is_equal):
|
||||
return '=='
|
||||
else:
|
||||
return '!='
|
||||
70
addons/gut/compare_result.gd
Normal file
70
addons/gut/compare_result.gd
Normal file
@@ -0,0 +1,70 @@
|
||||
var _are_equal = false
|
||||
var are_equal = false :
|
||||
get:
|
||||
return get_are_equal()
|
||||
set(val):
|
||||
set_are_equal(val)
|
||||
|
||||
var _summary = null
|
||||
var summary = null :
|
||||
get:
|
||||
return get_summary()
|
||||
set(val):
|
||||
set_summary(val)
|
||||
|
||||
var _max_differences = 30
|
||||
var max_differences = 30 :
|
||||
get:
|
||||
return get_max_differences()
|
||||
set(val):
|
||||
set_max_differences(val)
|
||||
|
||||
var _differences = {}
|
||||
var differences :
|
||||
get:
|
||||
return get_differences()
|
||||
set(val):
|
||||
set_differences(val)
|
||||
|
||||
func _block_set(which, val):
|
||||
push_error(str('cannot set ', which, ', value [', val, '] ignored.'))
|
||||
|
||||
func _to_string():
|
||||
return str(get_summary()) # could be null, gotta str it.
|
||||
|
||||
func get_are_equal():
|
||||
return _are_equal
|
||||
|
||||
func set_are_equal(r_eq):
|
||||
_are_equal = r_eq
|
||||
|
||||
func get_summary():
|
||||
return _summary
|
||||
|
||||
func set_summary(smry):
|
||||
_summary = smry
|
||||
|
||||
func get_total_count():
|
||||
pass
|
||||
|
||||
func get_different_count():
|
||||
pass
|
||||
|
||||
func get_short_summary():
|
||||
return summary
|
||||
|
||||
func get_max_differences():
|
||||
return _max_differences
|
||||
|
||||
func set_max_differences(max_diff):
|
||||
_max_differences = max_diff
|
||||
|
||||
func get_differences():
|
||||
return _differences
|
||||
|
||||
func set_differences(diffs):
|
||||
_block_set('differences', diffs)
|
||||
|
||||
func get_brackets():
|
||||
return null
|
||||
|
||||
64
addons/gut/diff_formatter.gd
Normal file
64
addons/gut/diff_formatter.gd
Normal file
@@ -0,0 +1,64 @@
|
||||
var _utils = load('res://addons/gut/utils.gd').get_instance()
|
||||
var _strutils = _utils.Strutils.new()
|
||||
const INDENT = ' '
|
||||
var _max_to_display = 30
|
||||
const ABSOLUTE_MAX_DISPLAYED = 10000
|
||||
const UNLIMITED = -1
|
||||
|
||||
|
||||
func _single_diff(diff, depth=0):
|
||||
var to_return = ""
|
||||
var brackets = diff.get_brackets()
|
||||
|
||||
if(brackets != null and !diff.are_equal):
|
||||
to_return = ''
|
||||
to_return += str(brackets.open, "\n",
|
||||
_strutils.indent_text(differences_to_s(diff.differences, depth), depth+1, INDENT), "\n",
|
||||
brackets.close)
|
||||
else:
|
||||
to_return = str(diff)
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func make_it(diff):
|
||||
var to_return = ''
|
||||
if(diff.are_equal):
|
||||
to_return = diff.summary
|
||||
else:
|
||||
if(_max_to_display == ABSOLUTE_MAX_DISPLAYED):
|
||||
to_return = str(diff.get_value_1(), ' != ', diff.get_value_2())
|
||||
else:
|
||||
to_return = diff.get_short_summary()
|
||||
to_return += str("\n", _strutils.indent_text(_single_diff(diff, 0), 1, ' '))
|
||||
return to_return
|
||||
|
||||
|
||||
func differences_to_s(differences, depth=0):
|
||||
var to_return = ''
|
||||
var keys = differences.keys()
|
||||
keys.sort()
|
||||
var limit = min(_max_to_display, differences.size())
|
||||
|
||||
for i in range(limit):
|
||||
var key = keys[i]
|
||||
to_return += str(key, ": ", _single_diff(differences[key], depth))
|
||||
|
||||
if(i != limit -1):
|
||||
to_return += "\n"
|
||||
|
||||
if(differences.size() > _max_to_display):
|
||||
to_return += str("\n\n... ", differences.size() - _max_to_display, " more.")
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func get_max_to_display():
|
||||
return _max_to_display
|
||||
|
||||
|
||||
func set_max_to_display(max_to_display):
|
||||
_max_to_display = max_to_display
|
||||
if(_max_to_display == UNLIMITED):
|
||||
_max_to_display = ABSOLUTE_MAX_DISPLAYED
|
||||
|
||||
158
addons/gut/diff_tool.gd
Normal file
158
addons/gut/diff_tool.gd
Normal file
@@ -0,0 +1,158 @@
|
||||
extends 'res://addons/gut/compare_result.gd'
|
||||
const INDENT = ' '
|
||||
enum {
|
||||
DEEP,
|
||||
SIMPLE
|
||||
}
|
||||
|
||||
var _utils = load('res://addons/gut/utils.gd').get_instance()
|
||||
var _strutils = _utils.Strutils.new()
|
||||
var _compare = _utils.Comparator.new()
|
||||
var DiffTool = load('res://addons/gut/diff_tool.gd')
|
||||
|
||||
var _value_1 = null
|
||||
var _value_2 = null
|
||||
var _total_count = 0
|
||||
var _diff_type = null
|
||||
var _brackets = null
|
||||
var _valid = true
|
||||
var _desc_things = 'somethings'
|
||||
|
||||
# -------- comapre_result.gd "interface" ---------------------
|
||||
func set_are_equal(val):
|
||||
_block_set('are_equal', val)
|
||||
|
||||
func get_are_equal():
|
||||
if(!_valid):
|
||||
return null
|
||||
else:
|
||||
return differences.size() == 0
|
||||
|
||||
|
||||
func set_summary(val):
|
||||
_block_set('summary', val)
|
||||
|
||||
func get_summary():
|
||||
return summarize()
|
||||
|
||||
func get_different_count():
|
||||
return differences.size()
|
||||
|
||||
func get_total_count():
|
||||
return _total_count
|
||||
|
||||
func get_short_summary():
|
||||
var text = str(_strutils.truncate_string(str(_value_1), 50),
|
||||
' ', _compare.get_compare_symbol(are_equal), ' ',
|
||||
_strutils.truncate_string(str(_value_2), 50))
|
||||
if(!are_equal):
|
||||
text += str(' ', get_different_count(), ' of ', get_total_count(),
|
||||
' ', _desc_things, ' do not match.')
|
||||
return text
|
||||
|
||||
func get_brackets():
|
||||
return _brackets
|
||||
# -------- comapre_result.gd "interface" ---------------------
|
||||
|
||||
|
||||
func _invalidate():
|
||||
_valid = false
|
||||
differences = null
|
||||
|
||||
|
||||
func _init(v1,v2,diff_type=DEEP):
|
||||
_value_1 = v1
|
||||
_value_2 = v2
|
||||
_diff_type = diff_type
|
||||
_compare.set_should_compare_int_to_float(false)
|
||||
_find_differences(_value_1, _value_2)
|
||||
|
||||
|
||||
func _find_differences(v1, v2):
|
||||
if(_utils.are_datatypes_same(v1, v2)):
|
||||
if(typeof(v1) == TYPE_ARRAY):
|
||||
_brackets = {'open':'[', 'close':']'}
|
||||
_desc_things = 'indexes'
|
||||
_diff_array(v1, v2)
|
||||
elif(typeof(v2) == TYPE_DICTIONARY):
|
||||
_brackets = {'open':'{', 'close':'}'}
|
||||
_desc_things = 'keys'
|
||||
_diff_dictionary(v1, v2)
|
||||
else:
|
||||
_invalidate()
|
||||
_utils.get_logger().error('Only Arrays and Dictionaries are supported.')
|
||||
else:
|
||||
_invalidate()
|
||||
_utils.get_logger().error('Only Arrays and Dictionaries are supported.')
|
||||
|
||||
|
||||
func _diff_array(a1, a2):
|
||||
_total_count = max(a1.size(), a2.size())
|
||||
for i in range(a1.size()):
|
||||
var result = null
|
||||
if(i < a2.size()):
|
||||
if(_diff_type == DEEP):
|
||||
result = _compare.deep(a1[i], a2[i])
|
||||
else:
|
||||
result = _compare.simple(a1[i], a2[i])
|
||||
else:
|
||||
result = _compare.simple(a1[i], _compare.MISSING, 'index')
|
||||
|
||||
if(!result.are_equal):
|
||||
differences[i] = result
|
||||
|
||||
if(a1.size() < a2.size()):
|
||||
for i in range(a1.size(), a2.size()):
|
||||
differences[i] = _compare.simple(_compare.MISSING, a2[i], 'index')
|
||||
|
||||
|
||||
func _diff_dictionary(d1, d2):
|
||||
var d1_keys = d1.keys()
|
||||
var d2_keys = d2.keys()
|
||||
|
||||
# Process all the keys in d1
|
||||
_total_count += d1_keys.size()
|
||||
for key in d1_keys:
|
||||
if(!d2.has(key)):
|
||||
differences[key] = _compare.simple(d1[key], _compare.MISSING, 'key')
|
||||
else:
|
||||
d2_keys.remove_at(d2_keys.find(key))
|
||||
|
||||
var result = null
|
||||
if(_diff_type == DEEP):
|
||||
result = _compare.deep(d1[key], d2[key])
|
||||
else:
|
||||
result = _compare.simple(d1[key], d2[key])
|
||||
|
||||
if(!result.are_equal):
|
||||
differences[key] = result
|
||||
|
||||
# Process all the keys in d2 that didn't exist in d1
|
||||
_total_count += d2_keys.size()
|
||||
for i in range(d2_keys.size()):
|
||||
differences[d2_keys[i]] = _compare.simple(_compare.MISSING, d2[d2_keys[i]], 'key')
|
||||
|
||||
|
||||
func summarize():
|
||||
var summary = ''
|
||||
|
||||
if(are_equal):
|
||||
summary = get_short_summary()
|
||||
else:
|
||||
var formatter = load('res://addons/gut/diff_formatter.gd').new()
|
||||
formatter.set_max_to_display(max_differences)
|
||||
summary = formatter.make_it(self)
|
||||
|
||||
return summary
|
||||
|
||||
|
||||
func get_diff_type():
|
||||
return _diff_type
|
||||
|
||||
|
||||
func get_value_1():
|
||||
return _value_1
|
||||
|
||||
|
||||
func get_value_2():
|
||||
return _value_2
|
||||
7
addons/gut/double_templates/function_template.txt
Normal file
7
addons/gut/double_templates/function_template.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
{func_decleration}
|
||||
{vararg_warning}__gutdbl.spy_on('{method_name}', {param_array})
|
||||
if(__gutdbl.should_call_super('{method_name}', {param_array})):
|
||||
return {super_call}
|
||||
else:
|
||||
return __gutdbl.get_stubbed_return('{method_name}', {param_array})
|
||||
|
||||
4
addons/gut/double_templates/init_template.txt
Normal file
4
addons/gut/double_templates/init_template.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
{func_decleration}:
|
||||
super({super_params})
|
||||
__gutdbl.spy_on('{method_name}', {param_array})
|
||||
|
||||
32
addons/gut/double_templates/script_template.txt
Normal file
32
addons/gut/double_templates/script_template.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
# ##############################################################################
|
||||
# Gut Doubled Script
|
||||
# ##############################################################################
|
||||
{extends}
|
||||
|
||||
{constants}
|
||||
|
||||
{properties}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# GUT stuff
|
||||
# ------------------------------------------------------------------------------
|
||||
var __gutdbl_values = {
|
||||
double = self,
|
||||
thepath = '{path}',
|
||||
subpath = '{subpath}',
|
||||
stubber = {stubber_id},
|
||||
spy = {spy_id},
|
||||
gut = {gut_id},
|
||||
from_singleton = '{singleton_name}',
|
||||
is_partial = {is_partial},
|
||||
doubled_methods = {doubled_methods},
|
||||
}
|
||||
var __gutdbl = load('res://addons/gut/double_tools.gd').new(__gutdbl_values)
|
||||
|
||||
# Here so other things can check for a method to know if this is a double.
|
||||
func __gutdbl_check_method__():
|
||||
pass
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Doubled Methods
|
||||
# ------------------------------------------------------------------------------
|
||||
65
addons/gut/double_tools.gd
Normal file
65
addons/gut/double_tools.gd
Normal file
@@ -0,0 +1,65 @@
|
||||
var thepath = ''
|
||||
var subpath = ''
|
||||
var stubber = null
|
||||
var spy = null
|
||||
var gut = null
|
||||
var from_singleton = null
|
||||
var is_partial = null
|
||||
var double = null
|
||||
|
||||
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):
|
||||
if(inst_id == -1):
|
||||
return null
|
||||
else:
|
||||
return instance_from_id(inst_id)
|
||||
|
||||
|
||||
func should_call_super(method_name, called_with):
|
||||
if(stubber != null):
|
||||
return stubber.should_call_super(double, method_name, called_with)
|
||||
else:
|
||||
return false
|
||||
|
||||
|
||||
func spy_on(method_name, called_with):
|
||||
if(spy != null):
|
||||
spy.add_call(double, method_name, called_with)
|
||||
|
||||
|
||||
func get_stubbed_return(method_name, called_with):
|
||||
if(stubber != null):
|
||||
return stubber.get_return(double, method_name, called_with)
|
||||
else:
|
||||
return null
|
||||
|
||||
|
||||
func default_val(method_name, p_index, default_val=NO_DEFAULT_VALUE):
|
||||
if(stubber != null):
|
||||
return stubber.get_default_value(double, method_name, p_index)
|
||||
else:
|
||||
return null
|
||||
|
||||
|
||||
func vararg_warning():
|
||||
if(gut != null):
|
||||
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.")
|
||||
355
addons/gut/doubler.gd
Normal file
355
addons/gut/doubler.gd
Normal file
@@ -0,0 +1,355 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# A stroke of genius if I do say so. This allows for doubling a scene without
|
||||
# having to write any files. By overloading the "instantiate" method we can
|
||||
# make whatever we want.
|
||||
# ------------------------------------------------------------------------------
|
||||
class PackedSceneDouble:
|
||||
extends PackedScene
|
||||
var _script = null
|
||||
var _scene = null
|
||||
|
||||
func set_script_obj(obj):
|
||||
_script = obj
|
||||
|
||||
func instantiate(edit_state=0):
|
||||
var inst = _scene.instantiate(edit_state)
|
||||
var export_props = []
|
||||
var script_export_flag = (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE)
|
||||
|
||||
if(_script != null):
|
||||
if(inst.get_script() != null):
|
||||
# Get all the exported props and values so we can set them again
|
||||
for prop in inst.get_property_list():
|
||||
var is_export = prop.usage & (script_export_flag) == script_export_flag
|
||||
if(is_export):
|
||||
export_props.append([prop.name, inst.get(prop.name)])
|
||||
|
||||
inst.set_script(_script)
|
||||
for exported_value in export_props:
|
||||
inst.set(exported_value[0], exported_value[1])
|
||||
|
||||
return inst
|
||||
|
||||
func load_scene(path):
|
||||
_scene = load(path)
|
||||
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# START Doubler
|
||||
# ------------------------------------------------------------------------------
|
||||
var _utils = load('res://addons/gut/utils.gd').get_instance()
|
||||
var _base_script_text = _utils.get_file_as_text('res://addons/gut/double_templates/script_template.txt')
|
||||
var _script_collector = _utils.ScriptCollector.new()
|
||||
# used by tests for debugging purposes.
|
||||
var print_source = false
|
||||
var inner_class_registry = _utils.InnerClassRegistry.new()
|
||||
|
||||
# ###############
|
||||
# Properties
|
||||
# ###############
|
||||
var _stubber = _utils.Stubber.new()
|
||||
func get_stubber():
|
||||
return _stubber
|
||||
func set_stubber(stubber):
|
||||
_stubber = stubber
|
||||
|
||||
var _lgr = _utils.get_logger()
|
||||
func get_logger():
|
||||
return _lgr
|
||||
func set_logger(logger):
|
||||
_lgr = logger
|
||||
_method_maker.set_logger(logger)
|
||||
|
||||
var _spy = null
|
||||
func get_spy():
|
||||
return _spy
|
||||
func set_spy(spy):
|
||||
_spy = spy
|
||||
|
||||
var _gut = null
|
||||
func get_gut():
|
||||
return _gut
|
||||
func set_gut(gut):
|
||||
_gut = gut
|
||||
|
||||
var _strategy = null
|
||||
func get_strategy():
|
||||
return _strategy
|
||||
func set_strategy(strategy):
|
||||
if(GutUtils.DOUBLE_STRATEGY.values().has(strategy)):
|
||||
_strategy = strategy
|
||||
else:
|
||||
_lgr.error(str('doubler.gd: invalid double strategy ', strategy))
|
||||
|
||||
|
||||
var _method_maker = _utils.MethodMaker.new()
|
||||
func get_method_maker():
|
||||
return _method_maker
|
||||
|
||||
var _ignored_methods = _utils.OneToMany.new()
|
||||
func get_ignored_methods():
|
||||
return _ignored_methods
|
||||
|
||||
# ###############
|
||||
# Private
|
||||
# ###############
|
||||
func _init(strategy=_utils.DOUBLE_STRATEGY.SCRIPT_ONLY):
|
||||
set_logger(_utils.get_logger())
|
||||
_strategy = strategy
|
||||
|
||||
|
||||
func _get_indented_line(indents, text):
|
||||
var to_return = ''
|
||||
for _i in range(indents):
|
||||
to_return += "\t"
|
||||
return str(to_return, text, "\n")
|
||||
|
||||
|
||||
func _stub_to_call_super(parsed, method_name):
|
||||
if(!parsed.get_method(method_name).is_eligible_for_doubling()):
|
||||
return
|
||||
|
||||
var params = _utils.StubParams.new(parsed.script_path, method_name, parsed.subpath)
|
||||
params.to_call_super()
|
||||
_stubber.add_stub(params)
|
||||
|
||||
|
||||
func _get_base_script_text(parsed, override_path, partial, included_methods):
|
||||
var path = parsed.script_path
|
||||
if(override_path != null):
|
||||
path = override_path
|
||||
|
||||
var stubber_id = -1
|
||||
if(_stubber != null):
|
||||
stubber_id = _stubber.get_instance_id()
|
||||
|
||||
var spy_id = -1
|
||||
if(_spy != null):
|
||||
spy_id = _spy.get_instance_id()
|
||||
|
||||
var gut_id = -1
|
||||
if(_gut != null):
|
||||
gut_id = _gut.get_instance_id()
|
||||
|
||||
var extends_text = parsed.get_extends_text()
|
||||
|
||||
var values = {
|
||||
# Top sections
|
||||
"extends":extends_text,
|
||||
"constants":'',#obj_info.get_constants_text(),
|
||||
"properties":'',#obj_info.get_properties_text(),
|
||||
|
||||
# metadata values
|
||||
"path":path,
|
||||
"subpath":GutUtils.nvl(parsed.subpath, ''),
|
||||
"stubber_id":stubber_id,
|
||||
"spy_id":spy_id,
|
||||
"gut_id":gut_id,
|
||||
"singleton_name":'',#GutUtils.nvl(obj_info.get_singleton_name(), ''),
|
||||
"is_partial":partial,
|
||||
"doubled_methods":included_methods,
|
||||
}
|
||||
|
||||
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):
|
||||
var path = ""
|
||||
|
||||
path = parsed.script_path
|
||||
var dbl_src = ""
|
||||
var included_methods = []
|
||||
|
||||
for method in parsed.get_local_methods():
|
||||
if(_is_method_eligible_for_doubling(parsed, method)):
|
||||
included_methods.append(method.meta.name)
|
||||
var mthd = parsed.get_local_method(method.meta.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_NATIVE):
|
||||
for method in parsed.get_super_methods():
|
||||
if(_is_method_eligible_for_doubling(parsed, method)):
|
||||
included_methods.append(method.meta.name)
|
||||
_stub_to_call_super(parsed, method.meta.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):
|
||||
print(_utils.add_line_numbers(dbl_src))
|
||||
|
||||
var DblClass = _create_script_no_warnings(dbl_src)
|
||||
if(_stubber != null):
|
||||
_stub_method_default_values(DblClass, parsed, strategy)
|
||||
|
||||
return DblClass
|
||||
|
||||
|
||||
func _stub_method_default_values(which, parsed, strategy):
|
||||
for method in parsed.get_local_methods():
|
||||
if(method.is_eligible_for_doubling() && !_ignored_methods.has(parsed.resource, method.meta.name)):
|
||||
_stubber.stub_defaults_from_meta(parsed.script_path, method.meta)
|
||||
|
||||
|
||||
|
||||
func _double_scene_and_script(scene, strategy, partial):
|
||||
var to_return = PackedSceneDouble.new()
|
||||
to_return.load_scene(scene.get_path())
|
||||
|
||||
var script_obj = GutUtils.get_scene_script_object(scene)
|
||||
if(script_obj != null):
|
||||
var script_dbl = null
|
||||
if(partial):
|
||||
script_dbl = _partial_double(script_obj, strategy, scene.get_path())
|
||||
else:
|
||||
script_dbl = _double(script_obj, strategy, scene.get_path())
|
||||
to_return.set_script_obj(script_dbl)
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func _get_inst_id_ref_str(inst):
|
||||
var ref_str = 'null'
|
||||
if(inst):
|
||||
ref_str = str('instance_from_id(', inst.get_instance_id(),')')
|
||||
return ref_str
|
||||
|
||||
|
||||
func _get_func_text(method_hash, path):
|
||||
var override_count = null;
|
||||
if(_stubber != null):
|
||||
override_count = _stubber.get_parameter_count(path, method_hash.name)
|
||||
|
||||
var text = _method_maker.get_function_text(method_hash, override_count) + "\n"
|
||||
|
||||
return text
|
||||
|
||||
|
||||
func _parse_script(obj):
|
||||
var parsed = null
|
||||
|
||||
if(_utils.is_inner_class(obj)):
|
||||
if(inner_class_registry.has(obj)):
|
||||
parsed = _script_collector.parse(inner_class_registry.get_base_resource(obj), obj)
|
||||
else:
|
||||
_lgr.error('Doubling Inner Classes requires you register them first. Call register_inner_classes passing the script that contains the inner class.')
|
||||
else:
|
||||
parsed = _script_collector.parse(obj)
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
# Override path is used with scenes.
|
||||
func _double(obj, strategy, override_path=null):
|
||||
var parsed = _parse_script(obj)
|
||||
if(parsed != null):
|
||||
return _create_double(parsed, strategy, override_path, false)
|
||||
|
||||
|
||||
func _partial_double(obj, strategy, override_path=null):
|
||||
var parsed = _parse_script(obj)
|
||||
if(parsed != null):
|
||||
return _create_double(parsed, strategy, override_path, true)
|
||||
|
||||
|
||||
# -------------------------
|
||||
# Public
|
||||
# -------------------------
|
||||
|
||||
# double a script/object
|
||||
func double(obj, strategy=_strategy):
|
||||
return _double(obj, strategy)
|
||||
|
||||
func partial_double(obj, strategy=_strategy):
|
||||
return _partial_double(obj, strategy)
|
||||
|
||||
|
||||
# double a scene
|
||||
func double_scene(scene, strategy=_strategy):
|
||||
return _double_scene_and_script(scene, strategy, false)
|
||||
|
||||
func partial_double_scene(scene, strategy=_strategy):
|
||||
return _double_scene_and_script(scene, strategy, true)
|
||||
|
||||
|
||||
func double_gdnative(which):
|
||||
return _double(which, _utils.DOUBLE_STRATEGY.INCLUDE_NATIVE)
|
||||
|
||||
func partial_double_gdnative(which):
|
||||
return _partial_double(which, _utils.DOUBLE_STRATEGY.INCLUDE_NATIVE)
|
||||
|
||||
|
||||
func double_inner(parent, inner, strategy=_strategy):
|
||||
var parsed = _script_collector.parse(parent, inner)
|
||||
return _create_double(parsed, strategy, null, false)
|
||||
|
||||
|
||||
func partial_double_inner(parent, inner, strategy=_strategy):
|
||||
var parsed = _script_collector.parse(parent, inner)
|
||||
return _create_double(parsed, strategy, null, true)
|
||||
|
||||
|
||||
func add_ignored_method(obj, method_name):
|
||||
_ignored_methods.add(obj, method_name)
|
||||
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
#(G)odot (U)nit (T)est class
|
||||
#
|
||||
# ##############################################################################
|
||||
# The MIT License (MIT)
|
||||
# =====================
|
||||
#
|
||||
# Copyright (c) 2024 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.
|
||||
#
|
||||
# ##############################################################################
|
||||
BIN
addons/gut/fonts/AnonymousPro-Bold.ttf
Normal file
BIN
addons/gut/fonts/AnonymousPro-Bold.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/AnonymousPro-Bold.ttf.import
Normal file
33
addons/gut/fonts/AnonymousPro-Bold.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://c8axnpxc0nrk4"
|
||||
path="res://.godot/imported/AnonymousPro-Bold.ttf-9d8fef4d357af5b52cd60afbe608aa49.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/AnonymousPro-Bold.ttf"
|
||||
dest_files=["res://.godot/imported/AnonymousPro-Bold.ttf-9d8fef4d357af5b52cd60afbe608aa49.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/AnonymousPro-BoldItalic.ttf
Normal file
BIN
addons/gut/fonts/AnonymousPro-BoldItalic.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/AnonymousPro-BoldItalic.ttf.import
Normal file
33
addons/gut/fonts/AnonymousPro-BoldItalic.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://msst1l2s2s"
|
||||
path="res://.godot/imported/AnonymousPro-BoldItalic.ttf-4274bf704d3d6b9cd32c4f0754d8c83d.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/AnonymousPro-BoldItalic.ttf"
|
||||
dest_files=["res://.godot/imported/AnonymousPro-BoldItalic.ttf-4274bf704d3d6b9cd32c4f0754d8c83d.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/AnonymousPro-Italic.ttf
Normal file
BIN
addons/gut/fonts/AnonymousPro-Italic.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/AnonymousPro-Italic.ttf.import
Normal file
33
addons/gut/fonts/AnonymousPro-Italic.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://hf5rdg67jcwc"
|
||||
path="res://.godot/imported/AnonymousPro-Italic.ttf-9989590b02137b799e13d570de2a42c1.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/AnonymousPro-Italic.ttf"
|
||||
dest_files=["res://.godot/imported/AnonymousPro-Italic.ttf-9989590b02137b799e13d570de2a42c1.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/AnonymousPro-Regular.ttf
Normal file
BIN
addons/gut/fonts/AnonymousPro-Regular.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/AnonymousPro-Regular.ttf.import
Normal file
33
addons/gut/fonts/AnonymousPro-Regular.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://c6c7gnx36opr0"
|
||||
path="res://.godot/imported/AnonymousPro-Regular.ttf-856c843fd6f89964d2ca8d8ff1724f13.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/AnonymousPro-Regular.ttf"
|
||||
dest_files=["res://.godot/imported/AnonymousPro-Regular.ttf-856c843fd6f89964d2ca8d8ff1724f13.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/CourierPrime-Bold.ttf
Normal file
BIN
addons/gut/fonts/CourierPrime-Bold.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/CourierPrime-Bold.ttf.import
Normal file
33
addons/gut/fonts/CourierPrime-Bold.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://bhjgpy1dovmyq"
|
||||
path="res://.godot/imported/CourierPrime-Bold.ttf-1f003c66d63ebed70964e7756f4fa235.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/CourierPrime-Bold.ttf"
|
||||
dest_files=["res://.godot/imported/CourierPrime-Bold.ttf-1f003c66d63ebed70964e7756f4fa235.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/CourierPrime-BoldItalic.ttf
Normal file
BIN
addons/gut/fonts/CourierPrime-BoldItalic.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/CourierPrime-BoldItalic.ttf.import
Normal file
33
addons/gut/fonts/CourierPrime-BoldItalic.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://n6mxiov5sbgc"
|
||||
path="res://.godot/imported/CourierPrime-BoldItalic.ttf-65ebcc61dd5e1dfa8f96313da4ad7019.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/CourierPrime-BoldItalic.ttf"
|
||||
dest_files=["res://.godot/imported/CourierPrime-BoldItalic.ttf-65ebcc61dd5e1dfa8f96313da4ad7019.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/CourierPrime-Italic.ttf
Normal file
BIN
addons/gut/fonts/CourierPrime-Italic.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/CourierPrime-Italic.ttf.import
Normal file
33
addons/gut/fonts/CourierPrime-Italic.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://mcht266g817e"
|
||||
path="res://.godot/imported/CourierPrime-Italic.ttf-baa9156a73770735a0f72fb20b907112.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/CourierPrime-Italic.ttf"
|
||||
dest_files=["res://.godot/imported/CourierPrime-Italic.ttf-baa9156a73770735a0f72fb20b907112.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/CourierPrime-Regular.ttf
Normal file
BIN
addons/gut/fonts/CourierPrime-Regular.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/CourierPrime-Regular.ttf.import
Normal file
33
addons/gut/fonts/CourierPrime-Regular.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://bnh0lslf4yh87"
|
||||
path="res://.godot/imported/CourierPrime-Regular.ttf-3babe7e4a7a588dfc9a84c14b4f1fe23.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/CourierPrime-Regular.ttf"
|
||||
dest_files=["res://.godot/imported/CourierPrime-Regular.ttf-3babe7e4a7a588dfc9a84c14b4f1fe23.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/LobsterTwo-Bold.ttf
Normal file
BIN
addons/gut/fonts/LobsterTwo-Bold.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/LobsterTwo-Bold.ttf.import
Normal file
33
addons/gut/fonts/LobsterTwo-Bold.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://cmiuntu71oyl3"
|
||||
path="res://.godot/imported/LobsterTwo-Bold.ttf-7c7f734103b58a32491a4788186f3dcb.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/LobsterTwo-Bold.ttf"
|
||||
dest_files=["res://.godot/imported/LobsterTwo-Bold.ttf-7c7f734103b58a32491a4788186f3dcb.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/LobsterTwo-BoldItalic.ttf
Normal file
BIN
addons/gut/fonts/LobsterTwo-BoldItalic.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/LobsterTwo-BoldItalic.ttf.import
Normal file
33
addons/gut/fonts/LobsterTwo-BoldItalic.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://bll38n2ct6qme"
|
||||
path="res://.godot/imported/LobsterTwo-BoldItalic.ttf-227406a33e84448e6aa974176016de19.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/LobsterTwo-BoldItalic.ttf"
|
||||
dest_files=["res://.godot/imported/LobsterTwo-BoldItalic.ttf-227406a33e84448e6aa974176016de19.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/LobsterTwo-Italic.ttf
Normal file
BIN
addons/gut/fonts/LobsterTwo-Italic.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/LobsterTwo-Italic.ttf.import
Normal file
33
addons/gut/fonts/LobsterTwo-Italic.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://dis65h8wxc3f2"
|
||||
path="res://.godot/imported/LobsterTwo-Italic.ttf-f93abf6c25390c85ad5fb6c4ee75159e.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/LobsterTwo-Italic.ttf"
|
||||
dest_files=["res://.godot/imported/LobsterTwo-Italic.ttf-f93abf6c25390c85ad5fb6c4ee75159e.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
BIN
addons/gut/fonts/LobsterTwo-Regular.ttf
Normal file
BIN
addons/gut/fonts/LobsterTwo-Regular.ttf
Normal file
Binary file not shown.
33
addons/gut/fonts/LobsterTwo-Regular.ttf.import
Normal file
33
addons/gut/fonts/LobsterTwo-Regular.ttf.import
Normal file
@@ -0,0 +1,33 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://5e8msj0ih2pv"
|
||||
path="res://.godot/imported/LobsterTwo-Regular.ttf-f3fcfa01cd671c8da433dd875d0fe04b.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/fonts/LobsterTwo-Regular.ttf"
|
||||
dest_files=["res://.godot/imported/LobsterTwo-Regular.ttf-f3fcfa01cd671c8da433dd875d0fe04b.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=1
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
94
addons/gut/fonts/OFL.txt
Normal file
94
addons/gut/fonts/OFL.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Copyright (c) 2009, Mark Simonson (http://www.ms-studio.com, mark@marksimonson.com),
|
||||
with Reserved Font Name Anonymous Pro.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
141
addons/gut/gui/BottomPanelShortcuts.gd
Normal file
141
addons/gut/gui/BottomPanelShortcuts.gd
Normal file
@@ -0,0 +1,141 @@
|
||||
@tool
|
||||
extends Window
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
var default_path = GutEditorGlobals.editor_shortcuts_path
|
||||
|
||||
@onready var _ctrls = {
|
||||
run_all = $Layout/CRunAll/ShortcutButton,
|
||||
run_current_script = $Layout/CRunCurrentScript/ShortcutButton,
|
||||
run_current_inner = $Layout/CRunCurrentInner/ShortcutButton,
|
||||
run_current_test = $Layout/CRunCurrentTest/ShortcutButton,
|
||||
panel_button = $Layout/CPanelButton/ShortcutButton,
|
||||
}
|
||||
|
||||
var _user_prefs = GutEditorGlobals.user_prefs
|
||||
|
||||
func _ready():
|
||||
for key in _ctrls:
|
||||
var sc_button = _ctrls[key]
|
||||
sc_button.connect('start_edit', _on_edit_start.bind(sc_button))
|
||||
sc_button.connect('end_edit', _on_edit_end)
|
||||
|
||||
# show dialog when running scene from editor.
|
||||
if(get_parent() == get_tree().root):
|
||||
popup_centered()
|
||||
|
||||
|
||||
func _cancel_all():
|
||||
_ctrls.run_all.cancel()
|
||||
_ctrls.run_current_script.cancel()
|
||||
_ctrls.run_current_inner.cancel()
|
||||
_ctrls.run_current_test.cancel()
|
||||
_ctrls.panel_button.cancel()
|
||||
|
||||
# ------------
|
||||
# Events
|
||||
# ------------
|
||||
func _on_Hide_pressed():
|
||||
hide()
|
||||
|
||||
|
||||
func _on_edit_start(which):
|
||||
for key in _ctrls:
|
||||
var sc_button = _ctrls[key]
|
||||
if(sc_button != which):
|
||||
sc_button.disable_set(true)
|
||||
sc_button.disable_clear(true)
|
||||
|
||||
|
||||
func _on_edit_end():
|
||||
for key in _ctrls:
|
||||
var sc_button = _ctrls[key]
|
||||
sc_button.disable_set(false)
|
||||
sc_button.disable_clear(false)
|
||||
|
||||
|
||||
func _on_popup_hide():
|
||||
_cancel_all()
|
||||
|
||||
# ------------
|
||||
# Public
|
||||
# ------------
|
||||
func get_run_all():
|
||||
return _ctrls.run_all.get_shortcut()
|
||||
|
||||
func get_run_current_script():
|
||||
return _ctrls.run_current_script.get_shortcut()
|
||||
|
||||
func get_run_current_inner():
|
||||
return _ctrls.run_current_inner.get_shortcut()
|
||||
|
||||
func get_run_current_test():
|
||||
return _ctrls.run_current_test.get_shortcut()
|
||||
|
||||
func get_panel_button():
|
||||
return _ctrls.panel_button.get_shortcut()
|
||||
|
||||
func _set_pref_value(pref, button):
|
||||
pref.value = {shortcut = button.get_shortcut().events}
|
||||
|
||||
|
||||
func save_shortcuts():
|
||||
save_shortcuts_to_file(default_path)
|
||||
|
||||
|
||||
func save_shortcuts_to_editor_settings():
|
||||
_set_pref_value(_user_prefs.shortcut_run_all, _ctrls.run_all)
|
||||
_set_pref_value(_user_prefs.shortcut_run_current_script, _ctrls.run_current_script)
|
||||
_set_pref_value(_user_prefs.shortcut_run_current_inner, _ctrls.run_current_inner)
|
||||
_set_pref_value(_user_prefs.shortcut_run_current_test, _ctrls.run_current_test)
|
||||
_set_pref_value(_user_prefs.shortcut_panel_button, _ctrls.panel_button)
|
||||
|
||||
_user_prefs.save_it()
|
||||
|
||||
|
||||
func save_shortcuts_to_file(path):
|
||||
var f = ConfigFile.new()
|
||||
f.set_value('main', 'run_all', _ctrls.run_all.get_shortcut())
|
||||
f.set_value('main', 'run_current_script', _ctrls.run_current_script.get_shortcut())
|
||||
f.set_value('main', 'run_current_inner', _ctrls.run_current_inner.get_shortcut())
|
||||
f.set_value('main', 'run_current_test', _ctrls.run_current_test.get_shortcut())
|
||||
f.set_value('main', 'panel_button', _ctrls.panel_button.get_shortcut())
|
||||
f.save(path)
|
||||
|
||||
|
||||
func _load_shortcut_from_pref(user_pref):
|
||||
var to_return = Shortcut.new()
|
||||
# value with be _user_prefs.EMPTY which is a string when the value
|
||||
# has not been set.
|
||||
if(typeof(user_pref.value) == TYPE_DICTIONARY):
|
||||
to_return.events.append(user_pref.value.shortcut[0])
|
||||
# to_return = user_pref.value
|
||||
return to_return
|
||||
|
||||
|
||||
func load_shortcuts():
|
||||
load_shortcuts_from_file(default_path)
|
||||
|
||||
|
||||
func load_shortcuts_from_editor_settings():
|
||||
var empty = Shortcut.new()
|
||||
|
||||
_ctrls.run_all.set_shortcut(_load_shortcut_from_pref(_user_prefs.shortcut_run_all))
|
||||
_ctrls.run_current_script.set_shortcut(_load_shortcut_from_pref(_user_prefs.shortcut_run_current_script))
|
||||
_ctrls.run_current_inner.set_shortcut(_load_shortcut_from_pref(_user_prefs.shortcut_run_current_inner))
|
||||
_ctrls.run_current_test.set_shortcut(_load_shortcut_from_pref(_user_prefs.shortcut_run_current_test))
|
||||
_ctrls.panel_button.set_shortcut(_load_shortcut_from_pref(_user_prefs.shortcut_panel_button))
|
||||
|
||||
|
||||
func load_shortcuts_from_file(path):
|
||||
var f = ConfigFile.new()
|
||||
var empty = Shortcut.new()
|
||||
|
||||
f.load(path)
|
||||
_ctrls.run_all.set_shortcut(f.get_value('main', 'run_all', empty))
|
||||
_ctrls.run_current_script.set_shortcut(f.get_value('main', 'run_current_script', empty))
|
||||
_ctrls.run_current_inner.set_shortcut(f.get_value('main', 'run_current_inner', empty))
|
||||
_ctrls.run_current_test.set_shortcut(f.get_value('main', 'run_current_test', empty))
|
||||
_ctrls.panel_button.set_shortcut(f.get_value('main', 'panel_button', empty))
|
||||
|
||||
|
||||
153
addons/gut/gui/BottomPanelShortcuts.tscn
Normal file
153
addons/gut/gui/BottomPanelShortcuts.tscn
Normal file
@@ -0,0 +1,153 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bsk32dh41b4gs"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://sfb1fw8j6ufu" path="res://addons/gut/gui/ShortcutButton.tscn" id="1"]
|
||||
[ext_resource type="Script" path="res://addons/gut/gui/BottomPanelShortcuts.gd" id="2"]
|
||||
|
||||
[node name="BottomPanelShortcuts" type="Popup"]
|
||||
title = "Shortcuts"
|
||||
size = Vector2i(500, 350)
|
||||
visible = true
|
||||
exclusive = true
|
||||
unresizable = false
|
||||
borderless = false
|
||||
script = ExtResource("2")
|
||||
|
||||
[node name="Layout" type="VBoxContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 5.0
|
||||
offset_right = -5.0
|
||||
offset_bottom = 2.0
|
||||
|
||||
[node name="TopPad" type="CenterContainer" parent="Layout"]
|
||||
custom_minimum_size = Vector2(0, 5)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label2" type="Label" parent="Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
text = "Always Active"
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="Layout/Label2"]
|
||||
show_behind_parent = true
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
color = Color(0, 0, 0, 0.196078)
|
||||
|
||||
[node name="CPanelButton" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Layout/CPanelButton"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Show/Hide GUT Panel"
|
||||
|
||||
[node name="ShortcutButton" parent="Layout/CPanelButton" instance=ExtResource("1")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="GutPanelPad" type="CenterContainer" parent="Layout"]
|
||||
custom_minimum_size = Vector2(0, 5)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
text = "Only Active When GUT Panel Shown"
|
||||
|
||||
[node name="ColorRect2" type="ColorRect" parent="Layout/Label"]
|
||||
show_behind_parent = true
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
color = Color(0, 0, 0, 0.196078)
|
||||
|
||||
[node name="TopPad2" type="CenterContainer" parent="Layout"]
|
||||
custom_minimum_size = Vector2(0, 5)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CRunAll" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Layout/CRunAll"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run All"
|
||||
|
||||
[node name="ShortcutButton" parent="Layout/CRunAll" instance=ExtResource("1")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="CRunCurrentScript" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Layout/CRunCurrentScript"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run Current Script"
|
||||
|
||||
[node name="ShortcutButton" parent="Layout/CRunCurrentScript" instance=ExtResource("1")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="CRunCurrentInner" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Layout/CRunCurrentInner"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run Current Inner Class"
|
||||
|
||||
[node name="ShortcutButton" parent="Layout/CRunCurrentInner" instance=ExtResource("1")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="CRunCurrentTest" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Layout/CRunCurrentTest"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run Current Test"
|
||||
|
||||
[node name="ShortcutButton" parent="Layout/CRunCurrentTest" instance=ExtResource("1")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="CenterContainer2" type="CenterContainer" parent="Layout"]
|
||||
custom_minimum_size = Vector2(0, 5)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ShiftDisclaimer" type="Label" parent="Layout"]
|
||||
layout_mode = 2
|
||||
text = "\"Shift\" cannot be the only modifier for a shortcut."
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="Layout/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Hide" type="Button" parent="Layout/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(60, 30)
|
||||
layout_mode = 2
|
||||
text = "Close"
|
||||
|
||||
[node name="BottomPad" type="CenterContainer" parent="Layout"]
|
||||
custom_minimum_size = Vector2(0, 10)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[connection signal="popup_hide" from="." to="." method="_on_popup_hide"]
|
||||
[connection signal="pressed" from="Layout/HBoxContainer/Hide" to="." method="_on_Hide_pressed"]
|
||||
361
addons/gut/gui/GutBottomPanel.gd
Normal file
361
addons/gut/gui/GutBottomPanel.gd
Normal file
@@ -0,0 +1,361 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
var TestScript = load('res://addons/gut/test.gd')
|
||||
var GutConfigGui = load('res://addons/gut/gui/gut_config_gui.gd')
|
||||
var ScriptTextEditors = load('res://addons/gut/gui/script_text_editor_controls.gd')
|
||||
|
||||
|
||||
var _interface = null;
|
||||
var _is_running = false;
|
||||
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)
|
||||
var _panel_button = null
|
||||
var _last_selected_path = null
|
||||
var _user_prefs = null
|
||||
|
||||
|
||||
@onready var _ctrls = {
|
||||
output = $layout/RSplit/CResults/TabBar/OutputText.get_rich_text_edit(),
|
||||
output_ctrl = $layout/RSplit/CResults/TabBar/OutputText,
|
||||
run_button = $layout/ControlBar/RunAll,
|
||||
shortcuts_button = $layout/ControlBar/Shortcuts,
|
||||
|
||||
settings_button = $layout/ControlBar/Settings,
|
||||
run_results_button = $layout/ControlBar/RunResultsBtn,
|
||||
output_button = $layout/ControlBar/OutputBtn,
|
||||
|
||||
settings = $layout/RSplit/sc/Settings,
|
||||
shortcut_dialog = $BottomPanelShortcuts,
|
||||
light = $layout/RSplit/CResults/ControlBar/Light3D,
|
||||
results = {
|
||||
bar = $layout/RSplit/CResults/ControlBar,
|
||||
passing = $layout/RSplit/CResults/ControlBar/Passing/value,
|
||||
failing = $layout/RSplit/CResults/ControlBar/Failing/value,
|
||||
pending = $layout/RSplit/CResults/ControlBar/Pending/value,
|
||||
errors = $layout/RSplit/CResults/ControlBar/Errors/value,
|
||||
warnings = $layout/RSplit/CResults/ControlBar/Warnings/value,
|
||||
orphans = $layout/RSplit/CResults/ControlBar/Orphans/value
|
||||
},
|
||||
run_at_cursor = $layout/ControlBar/RunAtCursor,
|
||||
run_results = $layout/RSplit/CResults/TabBar/RunResults
|
||||
}
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
|
||||
func _ready():
|
||||
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)
|
||||
_apply_options_to_controls()
|
||||
|
||||
_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')
|
||||
|
||||
_ctrls.run_results.set_output_control(_ctrls.output_ctrl)
|
||||
|
||||
var check_import = load('res://addons/gut/images/red.png')
|
||||
if(check_import == null):
|
||||
_ctrls.run_results.add_centered_text("GUT got some new images that are not imported yet. Please restart Godot.")
|
||||
print('GUT got some new images that are not imported yet. Please restart Godot.')
|
||||
else:
|
||||
_ctrls.run_results.add_centered_text("Let's run some tests!")
|
||||
|
||||
|
||||
func _apply_options_to_controls():
|
||||
hide_settings(_user_prefs.hide_settings.value)
|
||||
hide_result_tree(_user_prefs.hide_result_tree.value)
|
||||
hide_output_text(_user_prefs.hide_output_text.value)
|
||||
_ctrls.run_results.set_show_orphans(!_gut_config.options.hide_orphans)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if(_is_running):
|
||||
if(!_interface.is_playing_scene()):
|
||||
_is_running = false
|
||||
_ctrls.output_ctrl.add_text("\ndone")
|
||||
load_result_output()
|
||||
_gut_plugin.make_bottom_panel_item_visible(self)
|
||||
|
||||
# ---------------
|
||||
# Private
|
||||
# ---------------
|
||||
|
||||
func load_shortcuts():
|
||||
_ctrls.shortcut_dialog.load_shortcuts()
|
||||
_apply_shortcuts()
|
||||
|
||||
|
||||
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):
|
||||
_ctrls.output_ctrl.clear()
|
||||
var text = "Cannot run tests, you have a configuration error:\n"
|
||||
for e in errs:
|
||||
text += str('* ', e, "\n")
|
||||
text += "Check your settings ----->"
|
||||
_ctrls.output_ctrl.add_text(text)
|
||||
hide_output_text(false)
|
||||
hide_settings(false)
|
||||
|
||||
|
||||
func _save_config():
|
||||
_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.save_it()
|
||||
|
||||
_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_tests():
|
||||
GutEditorGlobals.create_temp_directory()
|
||||
|
||||
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')
|
||||
_save_config()
|
||||
_apply_options_to_controls()
|
||||
|
||||
_ctrls.output_ctrl.clear()
|
||||
_ctrls.run_results.clear()
|
||||
_ctrls.run_results.add_centered_text('Running...')
|
||||
|
||||
_interface.play_custom_scene('res://addons/gut/gui/GutRunner.tscn')
|
||||
_is_running = true
|
||||
_ctrls.output_ctrl.add_text('Running...')
|
||||
|
||||
|
||||
func _apply_shortcuts():
|
||||
_ctrls.run_button.shortcut = _ctrls.shortcut_dialog.get_run_all()
|
||||
|
||||
_ctrls.run_at_cursor.get_script_button().shortcut = \
|
||||
_ctrls.shortcut_dialog.get_run_current_script()
|
||||
_ctrls.run_at_cursor.get_inner_button().shortcut = \
|
||||
_ctrls.shortcut_dialog.get_run_current_inner()
|
||||
_ctrls.run_at_cursor.get_test_button().shortcut = \
|
||||
_ctrls.shortcut_dialog.get_run_current_test()
|
||||
|
||||
_panel_button.shortcut = _ctrls.shortcut_dialog.get_panel_button()
|
||||
|
||||
|
||||
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_editor_script_changed(script):
|
||||
if(script):
|
||||
set_current_script(script)
|
||||
|
||||
|
||||
func _on_RunAll_pressed():
|
||||
_run_all()
|
||||
|
||||
|
||||
func _on_Shortcuts_pressed():
|
||||
_ctrls.shortcut_dialog.popup_centered()
|
||||
|
||||
func _on_bottom_panel_shortcuts_visibility_changed():
|
||||
_apply_shortcuts()
|
||||
_ctrls.shortcut_dialog.save_shortcuts()
|
||||
|
||||
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.test_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
|
||||
|
||||
# ---------------
|
||||
# Public
|
||||
# ---------------
|
||||
func hide_result_tree(should):
|
||||
_ctrls.run_results.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):
|
||||
$layout/RSplit/CResults/TabBar/OutputText.visible = !should
|
||||
_ctrls.output_button.button_pressed = !should
|
||||
|
||||
|
||||
func load_result_output():
|
||||
_ctrls.output_ctrl.load_file(GutEditorGlobals.editor_run_bbcode_results_path)
|
||||
|
||||
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()
|
||||
|
||||
_ctrls.run_results.load_json_results(results)
|
||||
|
||||
var summary_json = results['test_scripts']['props']
|
||||
_ctrls.results.passing.text = str(summary_json.passing)
|
||||
_ctrls.results.passing.get_parent().visible = true
|
||||
|
||||
_ctrls.results.failing.text = str(summary_json.failures)
|
||||
_ctrls.results.failing.get_parent().visible = true
|
||||
|
||||
_ctrls.results.pending.text = str(summary_json.pending)
|
||||
_ctrls.results.pending.get_parent().visible = _ctrls.results.pending.text != '0'
|
||||
|
||||
_ctrls.results.errors.text = str(summary_json.errors)
|
||||
_ctrls.results.errors.get_parent().visible = _ctrls.results.errors.text != '0'
|
||||
|
||||
_ctrls.results.warnings.text = str(summary_json.warnings)
|
||||
_ctrls.results.warnings.get_parent().visible = _ctrls.results.warnings.text != '0'
|
||||
|
||||
_ctrls.results.orphans.text = str(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):
|
||||
_light_color = Color(1, 1, 0, .75)
|
||||
else:
|
||||
_light_color = Color(0, 1, 0, .75)
|
||||
_ctrls.light.visible = true
|
||||
_ctrls.light.queue_redraw()
|
||||
|
||||
|
||||
func set_current_script(script):
|
||||
if(script):
|
||||
if(_is_test_script(script)):
|
||||
var file = script.resource_path.get_file()
|
||||
_last_selected_path = script.resource_path.get_file()
|
||||
_ctrls.run_at_cursor.activate_for_script(script.resource_path)
|
||||
|
||||
|
||||
func set_interface(value):
|
||||
_interface = value
|
||||
_interface.get_script_editor().connect("editor_script_changed",Callable(self,'_on_editor_script_changed'))
|
||||
|
||||
var ste = ScriptTextEditors.new(_interface.get_script_editor())
|
||||
_ctrls.run_results.set_interface(_interface)
|
||||
_ctrls.run_results.set_script_text_editors(ste)
|
||||
_ctrls.run_at_cursor.set_script_text_editors(ste)
|
||||
set_current_script(_interface.get_script_editor().get_current_script())
|
||||
|
||||
|
||||
func set_plugin(value):
|
||||
_gut_plugin = value
|
||||
|
||||
|
||||
func set_panel_button(value):
|
||||
_panel_button = value
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Write a file.
|
||||
# ------------------------------------------------------------------------------
|
||||
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()
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Returns the text of a file or an empty string if the file could not be opened.
|
||||
# ------------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# return if_null if value is null otherwise return value
|
||||
# ------------------------------------------------------------------------------
|
||||
func nvl(value, if_null):
|
||||
if(value == null):
|
||||
return if_null
|
||||
else:
|
||||
return value
|
||||
250
addons/gut/gui/GutBottomPanel.tscn
Normal file
250
addons/gut/gui/GutBottomPanel.tscn
Normal file
@@ -0,0 +1,250 @@
|
||||
[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="PackedScene" uid="uid://bsk32dh41b4gs" path="res://addons/gut/gui/BottomPanelShortcuts.tscn" id="2"]
|
||||
[ext_resource type="PackedScene" uid="uid://0yunjxtaa8iw" path="res://addons/gut/gui/RunAtCursor.tscn" id="3"]
|
||||
[ext_resource type="Texture2D" uid="uid://cr6tvdv0ve6cv" path="res://addons/gut/gui/play.png" id="4"]
|
||||
[ext_resource type="PackedScene" uid="uid://4gyyn12um08h" path="res://addons/gut/gui/RunResults.tscn" id="5"]
|
||||
[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="Image" id="Image_4maas"]
|
||||
data = {
|
||||
"data": PackedByteArray(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, 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, 94, 94, 127, 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, 94, 94, 127, 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, 94, 94, 127, 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, 255, 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, 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, 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, 94, 94, 54, 255, 94, 94, 57, 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, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 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, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 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, 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, 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": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_umaha"]
|
||||
image = SubResource("Image_4maas")
|
||||
|
||||
[node name="GutBottomPanel" type="Control"]
|
||||
custom_minimum_size = Vector2(250, 250)
|
||||
layout_mode = 3
|
||||
anchor_left = -0.0025866
|
||||
anchor_top = -0.00176575
|
||||
anchor_right = 0.997413
|
||||
anchor_bottom = 0.998234
|
||||
offset_left = 2.64868
|
||||
offset_top = 1.05945
|
||||
offset_right = 2.64862
|
||||
offset_bottom = 1.05945
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="layout" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="ControlBar" type="HBoxContainer" parent="layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RunAll" type="Button" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 11
|
||||
shortcut = SubResource("9")
|
||||
text = "Run All"
|
||||
icon = ExtResource("4")
|
||||
|
||||
[node name="Label" type="Label" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
text = "Current: "
|
||||
|
||||
[node name="RunAtCursor" parent="layout/ControlBar" instance=ExtResource("3")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CenterContainer2" type="CenterContainer" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Sep1" type="ColorRect" parent="layout/ControlBar"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RunResultsBtn" type="Button" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
icon = SubResource("ImageTexture_umaha")
|
||||
|
||||
[node name="OutputBtn" type="Button" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
icon = SubResource("ImageTexture_umaha")
|
||||
|
||||
[node name="Settings" type="Button" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
icon = SubResource("ImageTexture_umaha")
|
||||
|
||||
[node name="Sep2" type="ColorRect" parent="layout/ControlBar"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Shortcuts" type="Button" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 11
|
||||
icon = SubResource("ImageTexture_umaha")
|
||||
|
||||
[node name="RSplit" type="HSplitContainer" parent="layout"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
collapsed = true
|
||||
|
||||
[node name="sc" type="ScrollContainer" parent="layout/RSplit"]
|
||||
visible = false
|
||||
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="CResults" type="VBoxContainer" parent="layout/RSplit"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ControlBar" type="HBoxContainer" parent="layout/RSplit/CResults"]
|
||||
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"]
|
||||
custom_minimum_size = Vector2(30, 30)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Passing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
||||
layout_mode = 2
|
||||
text = "Passing"
|
||||
|
||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"]
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Failing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
||||
layout_mode = 2
|
||||
text = "Failing"
|
||||
|
||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"]
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Pending" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
||||
layout_mode = 2
|
||||
text = "Pending"
|
||||
|
||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"]
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Orphans" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
||||
layout_mode = 2
|
||||
text = "Orphans"
|
||||
|
||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"]
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Errors" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
||||
layout_mode = 2
|
||||
text = "Errors"
|
||||
|
||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"]
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Warnings" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
||||
layout_mode = 2
|
||||
text = "Warnings"
|
||||
|
||||
[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"]
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="layout/RSplit/CResults/ControlBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="TabBar" type="HSplitContainer" parent="layout/RSplit/CResults"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="RunResults" parent="layout/RSplit/CResults/TabBar" instance=ExtResource("5")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="OutputText" parent="layout/RSplit/CResults/TabBar" instance=ExtResource("6")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="BottomPanelShortcuts" parent="." instance=ExtResource("2")]
|
||||
visible = false
|
||||
|
||||
[connection signal="pressed" from="layout/ControlBar/RunAll" to="." method="_on_RunAll_pressed"]
|
||||
[connection signal="run_tests" from="layout/ControlBar/RunAtCursor" to="." method="_on_RunAtCursor_run_tests"]
|
||||
[connection signal="pressed" from="layout/ControlBar/RunResultsBtn" to="." method="_on_RunResultsBtn_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar/OutputBtn" to="." method="_on_OutputBtn_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar/Settings" to="." method="_on_Settings_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar/Shortcuts" to="." method="_on_Shortcuts_pressed"]
|
||||
[connection signal="draw" from="layout/RSplit/CResults/ControlBar/Light3D" to="." method="_on_Light_draw"]
|
||||
[connection signal="visibility_changed" from="BottomPanelShortcuts" to="." method="_on_bottom_panel_shortcuts_visibility_changed"]
|
||||
326
addons/gut/gui/GutControl.gd
Normal file
326
addons/gut/gui/GutControl.gd
Normal file
@@ -0,0 +1,326 @@
|
||||
@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.ran_from_editor = false
|
||||
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():
|
||||
_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_options(path)
|
||||
_config.options.selected = ''
|
||||
_config.options.inner_class_name = ''
|
||||
_config.options.unit_test_name = ''
|
||||
_config_gui.load_file(path)
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
# 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"]
|
||||
167
addons/gut/gui/GutRunner.gd
Normal file
167
addons/gut/gui/GutRunner.gd
Normal file
@@ -0,0 +1,167 @@
|
||||
# ##############################################################################
|
||||
# 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 ran_from_editor to false before adding
|
||||
# this to the tree. To run tests manually, call run_tests.
|
||||
#
|
||||
# ##############################################################################
|
||||
extends Node2D
|
||||
|
||||
var Gut = load('res://addons/gut/gut.gd')
|
||||
var ResultExporter = load('res://addons/gut/result_exporter.gd')
|
||||
var GutConfig = load('res://addons/gut/gut_config.gd')
|
||||
|
||||
var runner_json_path = null
|
||||
var result_bbcode_path = null
|
||||
var result_json_path = null
|
||||
|
||||
var _gut_config = null
|
||||
var _gut = null;
|
||||
var _wrote_results = false
|
||||
|
||||
# The editor runs this scene using play_custom_scene, which means we cannot
|
||||
# pass any info directly to the scene. Whenever this is being used from
|
||||
# somewhere else, you probably want to set this to false before adding this
|
||||
# to the tree.
|
||||
var ran_from_editor = true
|
||||
|
||||
@onready var _gut_layer = $GutLayer
|
||||
@onready var _gui = $GutLayer/GutScene
|
||||
|
||||
|
||||
func _ready():
|
||||
# When used from the panel we have to kick off the tests ourselves b/c
|
||||
# there's no way I know of to interact with the scene that was run via
|
||||
# play_custom_scene.
|
||||
if(ran_from_editor):
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
runner_json_path = GutUtils.nvl(runner_json_path, GutEditorGlobals.editor_run_gut_config_path)
|
||||
result_bbcode_path = GutUtils.nvl(result_bbcode_path, GutEditorGlobals.editor_run_bbcode_results_path)
|
||||
result_json_path = GutUtils.nvl(result_json_path, GutEditorGlobals.editor_run_json_results_path)
|
||||
|
||||
if(_gut_config == null):
|
||||
_gut_config = GutConfig.new()
|
||||
_gut_config.load_options(runner_json_path)
|
||||
|
||||
call_deferred('run_tests')
|
||||
|
||||
|
||||
func _lazy_make_gut():
|
||||
if(_gut == null):
|
||||
_gut = Gut.new()
|
||||
|
||||
|
||||
func _setup_gui(show_gui):
|
||||
if(show_gui):
|
||||
_gui.gut = _gut
|
||||
var printer = _gut.logger.get_printer('gui')
|
||||
printer.set_textbox(_gui.get_textbox())
|
||||
else:
|
||||
_gut.logger.disable_printer('gui', true)
|
||||
_gui.visible = false
|
||||
|
||||
var opts = _gut_config.options
|
||||
_gui.set_font_size(opts.font_size)
|
||||
_gui.set_font(opts.font_name)
|
||||
if(opts.font_color != null and opts.font_color.is_valid_html_color()):
|
||||
_gui.set_default_font_color(Color(opts.font_color))
|
||||
if(opts.background_color != null and opts.background_color.is_valid_html_color()):
|
||||
_gui.set_background_color(Color(opts.background_color))
|
||||
|
||||
_gui.set_opacity(min(1.0, float(opts.opacity) / 100))
|
||||
_gui.use_compact_mode(opts.compact_mode)
|
||||
|
||||
|
||||
func _write_results():
|
||||
var content = _gui.get_textbox().get_parsed_text() #_gut.logger.get_gui_bbcode()
|
||||
var f = FileAccess.open(result_bbcode_path, FileAccess.WRITE)
|
||||
if(f != null):
|
||||
f.store_string(content)
|
||||
f = null # closes file
|
||||
else:
|
||||
push_error('Could not save bbcode, result = ', FileAccess.get_open_error())
|
||||
|
||||
var exporter = ResultExporter.new()
|
||||
var f_result = exporter.write_json_file(_gut, result_json_path)
|
||||
_wrote_results = true
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
if(!_wrote_results and ran_from_editor):
|
||||
_write_results()
|
||||
|
||||
|
||||
func _on_tests_finished(should_exit, should_exit_on_success):
|
||||
_write_results()
|
||||
|
||||
if(should_exit):
|
||||
get_tree().quit()
|
||||
elif(should_exit_on_success and _gut.get_fail_count() == 0):
|
||||
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(ran_from_editor):
|
||||
_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():
|
||||
_lazy_make_gut()
|
||||
return _gut
|
||||
|
||||
|
||||
func set_gut_config(which):
|
||||
_gut_config = which
|
||||
|
||||
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
# 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.
|
||||
#
|
||||
# ##############################################################################
|
||||
12
addons/gut/gui/GutRunner.tscn
Normal file
12
addons/gut/gui/GutRunner.tscn
Normal file
@@ -0,0 +1,12 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bqy3ikt6vu4b5"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gut/gui/GutRunner.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://m28heqtswbuq" path="res://addons/gut/GutScene.tscn" id="2_6ruxb"]
|
||||
|
||||
[node name="GutRunner" type="Node2D"]
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="GutLayer" type="CanvasLayer" parent="."]
|
||||
layer = 128
|
||||
|
||||
[node name="GutScene" parent="GutLayer" instance=ExtResource("2_6ruxb")]
|
||||
7
addons/gut/gui/GutSceneTheme.tres
Normal file
7
addons/gut/gui/GutSceneTheme.tres
Normal file
@@ -0,0 +1,7 @@
|
||||
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://cstkhwkpajvqu"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://c6c7gnx36opr0" path="res://addons/gut/fonts/AnonymousPro-Regular.ttf" id="1_df57p"]
|
||||
|
||||
[resource]
|
||||
default_font = ExtResource("1_df57p")
|
||||
Label/fonts/font = ExtResource("1_df57p")
|
||||
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
|
||||
365
addons/gut/gui/OutputText.gd
Normal file
365
addons/gut/gui/OutputText.gd
Normal file
@@ -0,0 +1,365 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
var PanelControls = load('res://addons/gut/gui/panel_controls.gd')
|
||||
|
||||
# ##############################################################################
|
||||
# Keeps search results from the TextEdit
|
||||
# ##############################################################################
|
||||
class TextEditSearcher:
|
||||
var te : TextEdit
|
||||
var _last_term = ''
|
||||
var _last_pos = Vector2(-1, -1)
|
||||
var _ignore_caret_change = false
|
||||
|
||||
func set_text_edit(which):
|
||||
te = which
|
||||
te.caret_changed.connect(_on_caret_changed)
|
||||
|
||||
|
||||
func _on_caret_changed():
|
||||
if(_ignore_caret_change):
|
||||
_ignore_caret_change = false
|
||||
else:
|
||||
_last_pos = _get_caret();
|
||||
|
||||
|
||||
func _get_caret():
|
||||
return Vector2(te.get_caret_column(), te.get_caret_line())
|
||||
|
||||
|
||||
func _set_caret_and_sel(pos, len):
|
||||
te.set_caret_line(pos.y)
|
||||
te.set_caret_column(pos.x)
|
||||
if(len > 0):
|
||||
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):
|
||||
_find(term, 0)
|
||||
|
||||
func find_prev(term):
|
||||
_find(term, te.SEARCH_BACKWARDS)
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
# Start OutputText control code
|
||||
# ##############################################################################
|
||||
@onready var _ctrls = {
|
||||
output = $Output,
|
||||
settings_bar = $Settings,
|
||||
use_colors = $Settings/UseColors,
|
||||
word_wrap = $Settings/WordWrap,
|
||||
|
||||
copy_button = $Toolbar/CopyButton,
|
||||
clear_button = $Toolbar/ClearButton,
|
||||
show_search = $Toolbar/ShowSearch,
|
||||
caret_position = $Toolbar/LblPosition,
|
||||
|
||||
search_bar = {
|
||||
bar = $Search,
|
||||
search_term = $Search/SearchTerm,
|
||||
}
|
||||
}
|
||||
|
||||
var _sr = TextEditSearcher.new()
|
||||
var _highlighter : CodeHighlighter
|
||||
var _font_name = null
|
||||
var _user_prefs = GutEditorGlobals.user_prefs
|
||||
var _font_name_pctrl = null
|
||||
var _font_size_pctrl = 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():
|
||||
_ctrls.use_colors.text = 'use colors'
|
||||
_ctrls.show_search.text = 'search'
|
||||
_ctrls.word_wrap.text = 'ww'
|
||||
|
||||
set_all_fonts("CourierPrime")
|
||||
set_font_size(30)
|
||||
|
||||
_ctrls.output.queue_redraw()
|
||||
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():
|
||||
_sr.set_text_edit(_ctrls.output)
|
||||
_ctrls.use_colors.icon = get_theme_icon('RichTextEffect', 'EditorIcons')
|
||||
_ctrls.show_search.icon = get_theme_icon('Search', 'EditorIcons')
|
||||
_ctrls.word_wrap.icon = get_theme_icon('Loop', 'EditorIcons')
|
||||
|
||||
_setup_colors()
|
||||
_ctrls.use_colors.button_pressed = true
|
||||
_use_highlighting(true)
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_test_running_setup()
|
||||
|
||||
_ctrls.settings_bar.visible = false
|
||||
_add_other_ctrls()
|
||||
|
||||
|
||||
func _add_other_ctrls():
|
||||
var fname = 'CourierNew'
|
||||
if(_user_prefs != null):
|
||||
fname = _user_prefs.output_font_name.value
|
||||
_font_name_pctrl = PanelControls.SelectControl.new('Font', fname, GutUtils.avail_fonts,
|
||||
"The font, you know, for the text below. Change it, see what it does.")
|
||||
_font_name_pctrl.changed.connect(_on_font_name_changed)
|
||||
_font_name_pctrl.label.size_flags_horizontal = SIZE_SHRINK_BEGIN
|
||||
_ctrls.settings_bar.add_child(_font_name_pctrl)
|
||||
set_all_fonts(fname)
|
||||
|
||||
var fsize = 30
|
||||
if(_user_prefs != null):
|
||||
fsize = _user_prefs.output_font_size.value
|
||||
_font_size_pctrl = PanelControls.NumberControl.new('Font Size', fsize , 5, 100,
|
||||
"The size of 'The Font'.")
|
||||
_font_size_pctrl.changed.connect(_on_font_size_changed)
|
||||
_font_size_pctrl.label.size_flags_horizontal = SIZE_SHRINK_BEGIN
|
||||
_ctrls.settings_bar.add_child(_font_size_pctrl)
|
||||
set_font_size(fsize)
|
||||
|
||||
|
||||
# ------------------
|
||||
# Private
|
||||
# ------------------
|
||||
|
||||
# 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 = [
|
||||
['Failed', Color.RED],
|
||||
['Passed', Color.GREEN],
|
||||
['Pending', Color.YELLOW],
|
||||
['Orphans', Color.YELLOW],
|
||||
['WARNING', Color.YELLOW],
|
||||
['ERROR', Color.RED]
|
||||
]
|
||||
|
||||
for keyword in keywords:
|
||||
to_return.add_keyword_color(keyword[0], keyword[1])
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func _setup_colors():
|
||||
_ctrls.output.clear()
|
||||
|
||||
var f_color = null
|
||||
if (_ctrls.output.theme == null) :
|
||||
f_color = get_theme_color("font_color")
|
||||
else :
|
||||
f_color = _ctrls.output.theme.font_color
|
||||
|
||||
_highlighter = _create_highlighter()
|
||||
_ctrls.output.queue_redraw()
|
||||
|
||||
|
||||
|
||||
func _use_highlighting(should):
|
||||
if(should):
|
||||
_ctrls.output.syntax_highlighter = _highlighter
|
||||
else:
|
||||
_ctrls.output.syntax_highlighter = null
|
||||
_refresh_output()
|
||||
|
||||
# ------------------
|
||||
# Events
|
||||
# ------------------
|
||||
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)
|
||||
|
||||
func _on_font_size_changed():
|
||||
set_font_size(_font_size_pctrl.value)
|
||||
if(_user_prefs != null):
|
||||
_user_prefs.output_font_size.value = _font_size_pctrl.value
|
||||
_user_prefs.output_font_size.save_it()
|
||||
|
||||
func _on_font_name_changed():
|
||||
set_all_fonts(_font_name_pctrl.text)
|
||||
if(_user_prefs != null):
|
||||
_user_prefs.output_font_name.value = _font_name_pctrl.text
|
||||
_user_prefs.output_font_name.save_it()
|
||||
|
||||
func _on_CopyButton_pressed():
|
||||
copy_to_clipboard()
|
||||
|
||||
func _on_UseColors_pressed():
|
||||
_use_highlighting(_ctrls.use_colors.button_pressed)
|
||||
|
||||
func _on_ClearButton_pressed():
|
||||
clear()
|
||||
|
||||
func _on_ShowSearch_pressed():
|
||||
show_search(_ctrls.show_search.button_pressed)
|
||||
|
||||
func _on_SearchTerm_focus_entered():
|
||||
_ctrls.search_bar.search_term.call_deferred('select_all')
|
||||
|
||||
func _on_SearchNext_pressed():
|
||||
_sr.find_next(_ctrls.search_bar.search_term.text)
|
||||
|
||||
func _on_SearchPrev_pressed():
|
||||
_sr.find_prev(_ctrls.search_bar.search_term.text)
|
||||
|
||||
func _on_SearchTerm_text_changed(new_text):
|
||||
if(new_text == ''):
|
||||
_ctrls.output.deselect()
|
||||
else:
|
||||
_sr.find_next(new_text)
|
||||
|
||||
func _on_SearchTerm_text_entered(new_text):
|
||||
if(Input.is_physical_key_pressed(KEY_SHIFT)):
|
||||
_sr.find_prev(new_text)
|
||||
else:
|
||||
_sr.find_next(new_text)
|
||||
|
||||
func _on_SearchTerm_gui_input(event):
|
||||
if(event is InputEventKey and !event.pressed and event.keycode == KEY_ESCAPE):
|
||||
show_search(false)
|
||||
|
||||
func _on_WordWrap_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()
|
||||
|
||||
func _on_settings_pressed():
|
||||
_ctrls.settings_bar.visible = $Toolbar/ShowSettings.button_pressed
|
||||
|
||||
# ------------------
|
||||
# Public
|
||||
# ------------------
|
||||
func show_search(should):
|
||||
_ctrls.search_bar.bar.visible = should
|
||||
if(should):
|
||||
_ctrls.search_bar.search_term.grab_focus()
|
||||
_ctrls.search_bar.search_term.select_all()
|
||||
_ctrls.show_search.button_pressed = should
|
||||
|
||||
|
||||
func search(text, start_pos, highlight=true):
|
||||
return _sr.find_next(text)
|
||||
|
||||
|
||||
func copy_to_clipboard():
|
||||
var selected = _ctrls.output.get_selected_text()
|
||||
if(selected != ''):
|
||||
DisplayServer.clipboard_set(selected)
|
||||
else:
|
||||
DisplayServer.clipboard_set(_ctrls.output.text)
|
||||
|
||||
|
||||
func clear():
|
||||
_ctrls.output.text = ''
|
||||
|
||||
|
||||
func _set_font(font_name, custom_name):
|
||||
var rtl = _ctrls.output
|
||||
if(font_name == null):
|
||||
rtl.remove_theme_font_override(custom_name)
|
||||
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):
|
||||
_font_name = GutUtils.nvl(base_name, 'Default')
|
||||
|
||||
if(base_name == 'Default'):
|
||||
_set_font(null, 'font')
|
||||
_set_font(null, 'normal_font')
|
||||
_set_font(null, 'bold_font')
|
||||
_set_font(null, 'italics_font')
|
||||
_set_font(null, 'bold_italics_font')
|
||||
else:
|
||||
_set_font(base_name + '-Regular', 'font')
|
||||
_set_font(base_name + '-Regular', 'normal_font')
|
||||
_set_font(base_name + '-Bold', 'bold_font')
|
||||
_set_font(base_name + '-Italic', 'italics_font')
|
||||
_set_font(base_name + '-BoldItalic', 'bold_italics_font')
|
||||
|
||||
|
||||
func set_font_size(new_size):
|
||||
_ctrls.output.set("theme_override_font_sizes/font_size", new_size)
|
||||
|
||||
|
||||
func set_use_colors(value):
|
||||
pass
|
||||
|
||||
|
||||
func get_use_colors():
|
||||
return false;
|
||||
|
||||
|
||||
func get_rich_text_edit():
|
||||
return _ctrls.output
|
||||
|
||||
|
||||
func load_file(path):
|
||||
var f = FileAccess.open(path, FileAccess.READ)
|
||||
if(f == null):
|
||||
return
|
||||
|
||||
var t = f.get_as_text()
|
||||
f = null # closes file
|
||||
_ctrls.output.text = t
|
||||
_ctrls.output.scroll_vertical = _ctrls.output.get_line_count()
|
||||
_ctrls.output.set_deferred('scroll_vertical', _ctrls.output.get_line_count())
|
||||
|
||||
|
||||
func add_text(text):
|
||||
if(is_inside_tree()):
|
||||
_ctrls.output.text += text
|
||||
|
||||
|
||||
func scroll_to_line(line):
|
||||
_ctrls.output.scroll_vertical = line
|
||||
_ctrls.output.set_caret_line(line)
|
||||
192
addons/gut/gui/OutputText.tscn
Normal file
192
addons/gut/gui/OutputText.tscn
Normal file
File diff suppressed because one or more lines are too long
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")
|
||||
348
addons/gut/gui/ResultsTree.gd
Normal file
348
addons/gut/gui/ResultsTree.gd
Normal file
@@ -0,0 +1,348 @@
|
||||
@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
|
||||
158
addons/gut/gui/RunAtCursor.gd
Normal file
158
addons/gut/gui/RunAtCursor.gd
Normal file
@@ -0,0 +1,158 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
var ScriptTextEditors = load('res://addons/gut/gui/script_text_editor_controls.gd')
|
||||
|
||||
@onready var _ctrls = {
|
||||
btn_script = $HBox/BtnRunScript,
|
||||
btn_inner = $HBox/BtnRunInnerClass,
|
||||
btn_method = $HBox/BtnRunMethod,
|
||||
lbl_none = $HBox/LblNoneSelected,
|
||||
arrow_1 = $HBox/Arrow1,
|
||||
arrow_2 = $HBox/Arrow2
|
||||
}
|
||||
|
||||
var _editors = null
|
||||
var _cur_editor = null
|
||||
var _last_line = -1
|
||||
var _cur_script_path = null
|
||||
var _last_info = {
|
||||
script = null,
|
||||
inner_class = null,
|
||||
test_method = null
|
||||
}
|
||||
|
||||
|
||||
signal run_tests(what)
|
||||
|
||||
|
||||
func _ready():
|
||||
_ctrls.lbl_none.visible = true
|
||||
_ctrls.btn_script.visible = false
|
||||
_ctrls.btn_inner.visible = false
|
||||
_ctrls.btn_method.visible = false
|
||||
_ctrls.arrow_1.visible = false
|
||||
_ctrls.arrow_2.visible = false
|
||||
|
||||
# ----------------
|
||||
# Private
|
||||
# ----------------
|
||||
func _set_editor(which):
|
||||
_last_line = -1
|
||||
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().caret_changed.disconnect(_on_cursor_changed)
|
||||
|
||||
if(which != null):
|
||||
_cur_editor = weakref(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_info = _editors.get_line_info()
|
||||
_update_buttons(_last_info)
|
||||
|
||||
|
||||
func _update_buttons(info):
|
||||
_ctrls.lbl_none.visible = _cur_script_path == null
|
||||
_ctrls.btn_script.visible = _cur_script_path != null
|
||||
|
||||
_ctrls.btn_inner.visible = info.inner_class != null
|
||||
_ctrls.arrow_1.visible = info.inner_class != null
|
||||
_ctrls.btn_inner.text = str(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.arrow_2.visible = info.test_method != null
|
||||
_ctrls.btn_method.text = str(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.
|
||||
# This appears to be what was causing the button to not be clickable the
|
||||
# first time.
|
||||
call_deferred("_update_size")
|
||||
|
||||
func _update_size():
|
||||
custom_minimum_size.x = _ctrls.btn_method.size.x + _ctrls.btn_method.position.x
|
||||
|
||||
# ----------------
|
||||
# Events
|
||||
# ----------------
|
||||
func _on_cursor_changed(which):
|
||||
if(which.get_caret_line() != _last_line):
|
||||
_last_line = which.get_caret_line()
|
||||
_last_info = _editors.get_line_info()
|
||||
_update_buttons(_last_info)
|
||||
|
||||
|
||||
func _on_BtnRunScript_pressed():
|
||||
var info = _last_info.duplicate()
|
||||
info.script = _cur_script_path.get_file()
|
||||
info.inner_class = null
|
||||
info.test_method = null
|
||||
emit_signal("run_tests", info)
|
||||
|
||||
|
||||
func _on_BtnRunInnerClass_pressed():
|
||||
var info = _last_info.duplicate()
|
||||
info.script = _cur_script_path.get_file()
|
||||
info.test_method = null
|
||||
emit_signal("run_tests", info)
|
||||
|
||||
|
||||
func _on_BtnRunMethod_pressed():
|
||||
var info = _last_info.duplicate()
|
||||
info.script = _cur_script_path.get_file()
|
||||
emit_signal("run_tests", info)
|
||||
|
||||
|
||||
# ----------------
|
||||
# Public
|
||||
# ----------------
|
||||
func set_script_text_editors(value):
|
||||
_editors = value
|
||||
|
||||
|
||||
func activate_for_script(path):
|
||||
_ctrls.btn_script.visible = true
|
||||
_ctrls.btn_script.text = path.get_file()
|
||||
_ctrls.btn_script.tooltip_text = str("Run all tests in script ", path)
|
||||
_cur_script_path = path
|
||||
_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())
|
||||
|
||||
|
||||
func get_script_button():
|
||||
return _ctrls.btn_script
|
||||
|
||||
|
||||
func get_inner_button():
|
||||
return _ctrls.btn_inner
|
||||
|
||||
|
||||
func get_test_button():
|
||||
return _ctrls.btn_method
|
||||
|
||||
|
||||
# not used, thought was configurable but it's just the script prefix
|
||||
func set_method_prefix(value):
|
||||
_editors.set_method_prefix(value)
|
||||
|
||||
|
||||
# not used, thought was configurable but it's just the script prefix
|
||||
func set_inner_class_prefix(value):
|
||||
_editors.set_inner_class_prefix(value)
|
||||
|
||||
|
||||
# Mashed this function in here b/c it has _editors. Probably should be
|
||||
# somewhere else (possibly in script_text_editor_controls).
|
||||
func search_current_editor_for_text(txt):
|
||||
var te = _editors.get_current_text_edit()
|
||||
var result = te.search(txt, 0, 0, 0)
|
||||
var to_return = -1
|
||||
|
||||
return to_return
|
||||
65
addons/gut/gui/RunAtCursor.tscn
Normal file
65
addons/gut/gui/RunAtCursor.tscn
Normal file
@@ -0,0 +1,65 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://0yunjxtaa8iw"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gut/gui/RunAtCursor.gd" id="1"]
|
||||
[ext_resource type="Texture2D" uid="uid://cr6tvdv0ve6cv" path="res://addons/gut/gui/play.png" id="2"]
|
||||
[ext_resource type="Texture2D" uid="uid://6wra5rxmfsrl" path="res://addons/gut/gui/arrow.png" id="3"]
|
||||
|
||||
[node name="RunAtCursor" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_right = 1.0
|
||||
offset_bottom = -527.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LblNoneSelected" type="Label" parent="HBox"]
|
||||
layout_mode = 2
|
||||
text = "<None>"
|
||||
|
||||
[node name="BtnRunScript" type="Button" parent="HBox"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "<script>"
|
||||
icon = ExtResource("2")
|
||||
|
||||
[node name="Arrow1" type="TextureButton" parent="HBox"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(24, 0)
|
||||
layout_mode = 2
|
||||
texture_normal = ExtResource("3")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="BtnRunInnerClass" type="Button" parent="HBox"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "<inner class>"
|
||||
icon = ExtResource("2")
|
||||
|
||||
[node name="Arrow2" type="TextureButton" parent="HBox"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(24, 0)
|
||||
layout_mode = 2
|
||||
texture_normal = ExtResource("3")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="BtnRunMethod" type="Button" parent="HBox"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "<method>"
|
||||
icon = ExtResource("2")
|
||||
|
||||
[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/BtnRunMethod" to="." method="_on_BtnRunMethod_pressed"]
|
||||
253
addons/gut/gui/RunResults.gd
Normal file
253
addons/gut/gui/RunResults.gd
Normal file
@@ -0,0 +1,253 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
|
||||
var _interface = null
|
||||
var _font = null
|
||||
var _font_size = null
|
||||
var _editors = null # script_text_editor_controls.gd
|
||||
var _output_control = null
|
||||
|
||||
@onready var _ctrls = {
|
||||
tree = $VBox/Output/Scroll/Tree,
|
||||
toolbar = {
|
||||
toolbar = $VBox/Toolbar,
|
||||
collapse = $VBox/Toolbar/Collapse,
|
||||
collapse_all = $VBox/Toolbar/CollapseAll,
|
||||
expand = $VBox/Toolbar/Expand,
|
||||
expand_all = $VBox/Toolbar/ExpandAll,
|
||||
hide_passing = $VBox/Toolbar/HidePassing,
|
||||
show_script = $VBox/Toolbar/ShowScript,
|
||||
scroll_output = $VBox/Toolbar/ScrollOutput
|
||||
}
|
||||
}
|
||||
|
||||
func _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():
|
||||
_ctrls.tree.hide_passing = true
|
||||
_ctrls.tree.show_orphans = true
|
||||
|
||||
_ctrls.toolbar.hide_passing.text = '[hp]'
|
||||
_ctrls.tree.load_json_file(GutEditorGlobals.editor_run_json_results_path)
|
||||
|
||||
|
||||
func _set_toolbutton_icon(btn, icon_name, text):
|
||||
if(Engine.is_editor_hint()):
|
||||
btn.icon = get_theme_icon(icon_name, 'EditorIcons')
|
||||
else:
|
||||
btn.text = str('[', text, ']')
|
||||
|
||||
|
||||
func _update_min_width():
|
||||
custom_minimum_size.x = _ctrls.toolbar.toolbar.size.x
|
||||
|
||||
|
||||
func _open_script_in_editor(path, line_number):
|
||||
if(_interface == null):
|
||||
print('Too soon, wait a bit and try again.')
|
||||
return
|
||||
|
||||
var r = load(path)
|
||||
if(line_number != null and line_number != -1):
|
||||
_interface.edit_script(r, line_number)
|
||||
else:
|
||||
_interface.edit_script(r)
|
||||
|
||||
if(_ctrls.toolbar.show_script.pressed):
|
||||
_interface.set_main_screen_editor('Script')
|
||||
|
||||
|
||||
# starts at beginning of text edit and searches for each search term, moving
|
||||
# through the text as it goes; ensuring that, when done, it found the first
|
||||
# occurance of the last srting that happend after the first occurance of
|
||||
# each string before it. (Generic way of searching for a method name in an
|
||||
# inner class that may have be a duplicate of a method name in a different
|
||||
# inner class)
|
||||
func _get_line_number_for_seq_search(search_strings, te):
|
||||
if(te == null):
|
||||
print("No Text editor to get line number for")
|
||||
return 0;
|
||||
|
||||
var result = null
|
||||
var line = Vector2i(0, 0)
|
||||
var s_flags = 0
|
||||
|
||||
var i = 0
|
||||
var string_found = true
|
||||
while(i < search_strings.size() and string_found):
|
||||
result = te.search(search_strings[i], s_flags, line.y, line.x)
|
||||
if(result.x != -1):
|
||||
line = result
|
||||
else:
|
||||
string_found = false
|
||||
i += 1
|
||||
|
||||
return line.y
|
||||
|
||||
|
||||
func _goto_code(path, line, method_name='', inner_class =''):
|
||||
if(_interface == null):
|
||||
print('going to ', [path, line, method_name, inner_class])
|
||||
return
|
||||
|
||||
_open_script_in_editor(path, line)
|
||||
if(line == -1):
|
||||
var search_strings = []
|
||||
if(inner_class != ''):
|
||||
search_strings.append(inner_class)
|
||||
|
||||
if(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())
|
||||
if(line != null and line != -1):
|
||||
_interface.get_script_editor().goto_line(line)
|
||||
|
||||
|
||||
func _goto_output(path, method_name, inner_class):
|
||||
if(_output_control == null):
|
||||
return
|
||||
|
||||
var search_strings = [path]
|
||||
|
||||
if(inner_class != ''):
|
||||
search_strings.append(inner_class)
|
||||
|
||||
if(method_name != ''):
|
||||
search_strings.append(method_name)
|
||||
|
||||
var line = _get_line_number_for_seq_search(search_strings, _output_control.get_rich_text_edit())
|
||||
if(line != null and line != -1):
|
||||
_output_control.scroll_to_line(line)
|
||||
|
||||
|
||||
|
||||
|
||||
# --------------
|
||||
# Events
|
||||
# --------------
|
||||
func _on_Collapse_pressed():
|
||||
collapse_selected()
|
||||
|
||||
|
||||
func _on_Expand_pressed():
|
||||
expand_selected()
|
||||
|
||||
|
||||
func _on_CollapseAll_pressed():
|
||||
collapse_all()
|
||||
|
||||
|
||||
func _on_ExpandAll_pressed():
|
||||
expand_all()
|
||||
|
||||
|
||||
func _on_Hide_Passing_pressed():
|
||||
_ctrls.tree.hide_passing = !_ctrls.toolbar.hide_passing.button_pressed
|
||||
_ctrls.tree.load_json_file(GutEditorGlobals.editor_run_json_results_path)
|
||||
|
||||
|
||||
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
|
||||
# --------------
|
||||
func add_centered_text(t):
|
||||
_ctrls.tree.add_centered_text(t)
|
||||
|
||||
|
||||
func clear_centered_text():
|
||||
_ctrls.tree.clear_centered_text()
|
||||
|
||||
|
||||
func clear():
|
||||
_ctrls.tree.clear()
|
||||
clear_centered_text()
|
||||
|
||||
|
||||
func set_interface(which):
|
||||
_interface = which
|
||||
|
||||
|
||||
func set_script_text_editors(value):
|
||||
_editors = value
|
||||
|
||||
|
||||
func collapse_all():
|
||||
_ctrls.tree.collapse_all()
|
||||
|
||||
|
||||
func expand_all():
|
||||
_ctrls.tree.expand_all()
|
||||
|
||||
|
||||
func collapse_selected():
|
||||
var item = _ctrls.tree.get_selected()
|
||||
if(item != null):
|
||||
_ctrls.tree.set_collapsed_on_all(item, true)
|
||||
|
||||
|
||||
func expand_selected():
|
||||
var item = _ctrls.tree.get_selected()
|
||||
if(item != null):
|
||||
_ctrls.tree.set_collapsed_on_all(item, false)
|
||||
|
||||
|
||||
func set_show_orphans(should):
|
||||
_ctrls.tree.show_orphans = should
|
||||
|
||||
|
||||
func set_font(font_name, size):
|
||||
pass
|
||||
# var dyn_font = FontFile.new()
|
||||
# var font_data = FontFile.new()
|
||||
# font_data.font_path = 'res://addons/gut/fonts/' + font_name + '-Regular.ttf'
|
||||
# font_data.antialiased = true
|
||||
# dyn_font.font_data = font_data
|
||||
#
|
||||
# _font = dyn_font
|
||||
# _font.size = size
|
||||
# _font_size = size
|
||||
|
||||
|
||||
func set_output_control(value):
|
||||
_output_control = value
|
||||
|
||||
|
||||
func load_json_results(j):
|
||||
_ctrls.tree.load_json_results(j)
|
||||
116
addons/gut/gui/RunResults.tscn
Normal file
116
addons/gut/gui/RunResults.tscn
Normal file
@@ -0,0 +1,116 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://4gyyn12um08h"]
|
||||
|
||||
[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="Image_abbh7"]
|
||||
data = {
|
||||
"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": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_x655i"]
|
||||
image = SubResource("Image_abbh7")
|
||||
|
||||
[node name="RunResults" type="Control"]
|
||||
custom_minimum_size = Vector2(345, 0)
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
offset_right = 709.0
|
||||
offset_bottom = 321.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="Toolbar" type="HBoxContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
|
||||
[node name="Expand" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
icon = SubResource("ImageTexture_x655i")
|
||||
|
||||
[node name="Collapse" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
icon = SubResource("ImageTexture_x655i")
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="VBox/Toolbar"]
|
||||
custom_minimum_size = Vector2(2, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LblAll" type="Label" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
text = "All:"
|
||||
|
||||
[node name="ExpandAll" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
icon = SubResource("ImageTexture_x655i")
|
||||
|
||||
[node name="CollapseAll" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
icon = SubResource("ImageTexture_x655i")
|
||||
|
||||
[node name="Sep2" type="ColorRect" parent="VBox/Toolbar"]
|
||||
custom_minimum_size = Vector2(2, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HidePassing" type="CheckBox" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
text = "Passing"
|
||||
|
||||
[node name="Sep3" type="ColorRect" parent="VBox/Toolbar"]
|
||||
custom_minimum_size = Vector2(2, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LblSync" type="Label" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
text = "Sync:"
|
||||
|
||||
[node name="ShowScript" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = SubResource("ImageTexture_x655i")
|
||||
|
||||
[node name="ScrollOutput" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = SubResource("ImageTexture_x655i")
|
||||
|
||||
[node name="Output" type="Panel" parent="VBox"]
|
||||
self_modulate = Color(1, 1, 1, 0.541176)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="VBox/Output"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Tree" parent="VBox/Output/Scroll" instance=ExtResource("2_o808v")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="FontSampler" type="Label" parent="."]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 14.0
|
||||
text = "000 of 000 passed"
|
||||
|
||||
[connection signal="pressed" from="VBox/Toolbar/Expand" to="." method="_on_Expand_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/Collapse" to="." method="_on_Collapse_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/ExpandAll" to="." method="_on_ExpandAll_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/CollapseAll" to="." method="_on_CollapseAll_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/HidePassing" to="." method="_on_Hide_Passing_pressed"]
|
||||
7
addons/gut/gui/Settings.tscn
Normal file
7
addons/gut/gui/Settings.tscn
Normal file
@@ -0,0 +1,7 @@
|
||||
[gd_scene format=3 uid="uid://cvvvtsah38l0e"]
|
||||
|
||||
[node name="Settings" type="VBoxContainer"]
|
||||
offset_right = 388.0
|
||||
offset_bottom = 586.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
152
addons/gut/gui/ShortcutButton.gd
Normal file
152
addons/gut/gui/ShortcutButton.gd
Normal file
@@ -0,0 +1,152 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
@onready var _ctrls = {
|
||||
shortcut_label = $Layout/lblShortcut,
|
||||
set_button = $Layout/SetButton,
|
||||
save_button = $Layout/SaveButton,
|
||||
cancel_button = $Layout/CancelButton,
|
||||
clear_button = $Layout/ClearButton
|
||||
}
|
||||
|
||||
signal changed
|
||||
signal start_edit
|
||||
signal end_edit
|
||||
|
||||
const NO_SHORTCUT = '<None>'
|
||||
|
||||
var _source_event = InputEventKey.new()
|
||||
var _pre_edit_event = null
|
||||
var _key_disp = NO_SHORTCUT
|
||||
var _editing = false
|
||||
|
||||
var _modifier_keys = [KEY_ALT, KEY_CTRL, KEY_META, KEY_SHIFT]
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
set_process_unhandled_key_input(false)
|
||||
|
||||
|
||||
func _display_shortcut():
|
||||
if(_key_disp == ''):
|
||||
_key_disp = NO_SHORTCUT
|
||||
_ctrls.shortcut_label.text = _key_disp
|
||||
|
||||
|
||||
func _is_shift_only_modifier():
|
||||
return _source_event.shift_pressed and \
|
||||
!(_source_event.alt_pressed or \
|
||||
_source_event.ctrl_pressed or \
|
||||
_source_event.meta_pressed) \
|
||||
and !_is_modifier(_source_event.keycode)
|
||||
|
||||
|
||||
func _has_modifier(event):
|
||||
return event.alt_pressed or event.ctrl_pressed or \
|
||||
event.meta_pressed or event.shift_pressed
|
||||
|
||||
|
||||
func _is_modifier(keycode):
|
||||
return _modifier_keys.has(keycode)
|
||||
|
||||
|
||||
func _edit_mode(should):
|
||||
_editing = should
|
||||
set_process_unhandled_key_input(should)
|
||||
_ctrls.set_button.visible = !should
|
||||
_ctrls.save_button.visible = should
|
||||
_ctrls.save_button.disabled = should
|
||||
_ctrls.cancel_button.visible = should
|
||||
_ctrls.clear_button.visible = !should
|
||||
|
||||
if(should and to_s() == ''):
|
||||
_ctrls.shortcut_label.text = 'press buttons'
|
||||
else:
|
||||
_ctrls.shortcut_label.text = to_s()
|
||||
|
||||
if(should):
|
||||
emit_signal("start_edit")
|
||||
else:
|
||||
emit_signal("end_edit")
|
||||
|
||||
# ---------------
|
||||
# Events
|
||||
# ---------------
|
||||
func _unhandled_key_input(event):
|
||||
if(event is InputEventKey):
|
||||
if(event.pressed):
|
||||
if(_has_modifier(event) and !_is_modifier(event.get_keycode_with_modifiers())):
|
||||
_source_event = event
|
||||
_key_disp = OS.get_keycode_string(event.get_keycode_with_modifiers())
|
||||
else:
|
||||
_source_event = InputEventKey.new()
|
||||
_key_disp = NO_SHORTCUT
|
||||
_display_shortcut()
|
||||
_ctrls.save_button.disabled = !is_valid()
|
||||
|
||||
|
||||
func _on_SetButton_pressed():
|
||||
_pre_edit_event = _source_event.duplicate(true)
|
||||
_edit_mode(true)
|
||||
|
||||
|
||||
func _on_SaveButton_pressed():
|
||||
_edit_mode(false)
|
||||
_pre_edit_event = null
|
||||
emit_signal('changed')
|
||||
|
||||
|
||||
func _on_CancelButton_pressed():
|
||||
cancel()
|
||||
|
||||
|
||||
func _on_ClearButton_pressed():
|
||||
clear_shortcut()
|
||||
|
||||
# ---------------
|
||||
# Public
|
||||
# ---------------
|
||||
func to_s():
|
||||
return OS.get_keycode_string(_source_event.get_keycode_with_modifiers())
|
||||
|
||||
|
||||
func is_valid():
|
||||
return _has_modifier(_source_event) and !_is_shift_only_modifier()
|
||||
|
||||
|
||||
func get_shortcut():
|
||||
var to_return = Shortcut.new()
|
||||
to_return.events.append(_source_event)
|
||||
return to_return
|
||||
|
||||
|
||||
func set_shortcut(sc):
|
||||
if(sc == null or sc.events == null || sc.events.size() <= 0):
|
||||
clear_shortcut()
|
||||
else:
|
||||
_source_event = sc.events[0]
|
||||
_key_disp = to_s()
|
||||
_display_shortcut()
|
||||
|
||||
|
||||
func clear_shortcut():
|
||||
_source_event = InputEventKey.new()
|
||||
_key_disp = NO_SHORTCUT
|
||||
_display_shortcut()
|
||||
|
||||
|
||||
func disable_set(should):
|
||||
_ctrls.set_button.disabled = should
|
||||
|
||||
|
||||
func disable_clear(should):
|
||||
_ctrls.clear_button.disabled = should
|
||||
|
||||
|
||||
func cancel():
|
||||
if(_editing):
|
||||
_edit_mode(false)
|
||||
_source_event = _pre_edit_event
|
||||
_key_disp = to_s()
|
||||
_display_shortcut()
|
||||
55
addons/gut/gui/ShortcutButton.tscn
Normal file
55
addons/gut/gui/ShortcutButton.tscn
Normal file
@@ -0,0 +1,55 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://sfb1fw8j6ufu"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/gut/gui/ShortcutButton.gd" id="1"]
|
||||
|
||||
[node name="ShortcutButton" type="Control"]
|
||||
custom_minimum_size = Vector2(210, 30)
|
||||
layout_mode = 3
|
||||
anchor_right = 0.123
|
||||
anchor_bottom = 0.04
|
||||
offset_right = 68.304
|
||||
offset_bottom = 6.08
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Layout" type="HBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="lblShortcut" type="Label" parent="Layout"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 7
|
||||
text = "<None>"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="Layout"]
|
||||
custom_minimum_size = Vector2(10, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SetButton" type="Button" parent="Layout"]
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "Set"
|
||||
|
||||
[node name="SaveButton" type="Button" parent="Layout"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "Save"
|
||||
|
||||
[node name="CancelButton" type="Button" parent="Layout"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
|
||||
[node name="ClearButton" type="Button" parent="Layout"]
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "Clear"
|
||||
|
||||
[connection signal="pressed" from="Layout/SetButton" to="." method="_on_SetButton_pressed"]
|
||||
[connection signal="pressed" from="Layout/SaveButton" to="." method="_on_SaveButton_pressed"]
|
||||
[connection signal="pressed" from="Layout/CancelButton" to="." method="_on_CancelButton_pressed"]
|
||||
[connection signal="pressed" from="Layout/ClearButton" to="." method="_on_ClearButton_pressed"]
|
||||
BIN
addons/gut/gui/arrow.png
Normal file
BIN
addons/gut/gui/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 B |
34
addons/gut/gui/arrow.png.import
Normal file
34
addons/gut/gui/arrow.png.import
Normal file
@@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://6wra5rxmfsrl"
|
||||
path="res://.godot/imported/arrow.png-2b5b2d838b5b3467cf300ac2da1630d9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/gui/arrow.png"
|
||||
dest_files=["res://.godot/imported/arrow.png-2b5b2d838b5b3467cf300ac2da1630d9.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
|
||||
47
addons/gut/gui/editor_globals.gd
Normal file
47
addons/gut/gui/editor_globals.gd
Normal file
@@ -0,0 +1,47 @@
|
||||
@tool
|
||||
|
||||
static var GutUserPreferences = load("res://addons/gut/gui/gut_user_preferences.gd")
|
||||
static var temp_directory = 'user://gut_temp_directory'
|
||||
|
||||
static var editor_run_gut_config_path = 'gut_editor_config.json':
|
||||
# This avoids having to use path_join wherever we want to reference this
|
||||
# path. The value is not supposed to change. Could it be a constant
|
||||
# instead? Probably, but I didn't like repeating the directory part.
|
||||
# Do I like that this is a bit witty. Absolutely.
|
||||
get: return temp_directory.path_join(editor_run_gut_config_path)
|
||||
# Should this print a message or something instead? Probably, but then I'd
|
||||
# be repeating even more code than if this was just a constant. So I didn't,
|
||||
# even though I wanted to put make the message a easter eggish fun message.
|
||||
# I didn't, so this dumb comment will have to serve as the easter eggish fun.
|
||||
set(v): pass
|
||||
|
||||
|
||||
static var editor_run_bbcode_results_path = 'gut_editor.bbcode':
|
||||
get: return temp_directory.path_join(editor_run_bbcode_results_path)
|
||||
set(v): pass
|
||||
|
||||
|
||||
static var editor_run_json_results_path = 'gut_editor.json':
|
||||
get: return temp_directory.path_join(editor_run_json_results_path)
|
||||
set(v): pass
|
||||
|
||||
|
||||
static var editor_shortcuts_path = 'gut_editor_shortcuts.cfg' :
|
||||
get: return temp_directory.path_join(editor_shortcuts_path)
|
||||
set(v): pass
|
||||
|
||||
|
||||
static var _user_prefs = null
|
||||
static var user_prefs = _user_prefs :
|
||||
# workaround not being able to reference EditorInterface when not in
|
||||
# the editor. This shouldn't be referenced by anything not in the
|
||||
# editor.
|
||||
get:
|
||||
if(_user_prefs == null and Engine.is_editor_hint()):
|
||||
_user_prefs = GutUserPreferences.new(EditorInterface.get_editor_settings())
|
||||
return _user_prefs
|
||||
|
||||
|
||||
static func create_temp_directory():
|
||||
DirAccess.make_dir_recursive_absolute(temp_directory)
|
||||
|
||||
346
addons/gut/gui/gut_config_gui.gd
Normal file
346
addons/gut/gui/gut_config_gui.gd
Normal file
@@ -0,0 +1,346 @@
|
||||
var PanelControls = load("res://addons/gut/gui/panel_controls.gd")
|
||||
var GutConfig = load('res://addons/gut/gut_config.gd')
|
||||
|
||||
const DIRS_TO_LIST = 6
|
||||
|
||||
var _base_container = null
|
||||
# All the various PanelControls indexed by thier gut_config keys.
|
||||
var _cfg_ctrls = {}
|
||||
|
||||
# specific titles that we need to do stuff with
|
||||
var _titles = {
|
||||
dirs = null
|
||||
}
|
||||
# All titles so we can free them when we want.
|
||||
var _all_titles = []
|
||||
|
||||
|
||||
func _init(cont):
|
||||
_base_container = cont
|
||||
|
||||
|
||||
func _add_title(text):
|
||||
var row = PanelControls.BaseGutPanelControl.new(text, text)
|
||||
_base_container.add_child(row)
|
||||
row.connect('draw', _on_title_cell_draw.bind(row))
|
||||
_all_titles.append(row)
|
||||
return row
|
||||
|
||||
func _add_ctrl(key, ctrl):
|
||||
_cfg_ctrls[key] = ctrl
|
||||
_base_container.add_child(ctrl)
|
||||
|
||||
|
||||
func _add_number(key, value, disp_text, v_min, v_max, hint=''):
|
||||
var ctrl = PanelControls.NumberControl.new(disp_text, value, v_min, v_max, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_select(key, value, values, disp_text, hint=''):
|
||||
var ctrl = PanelControls.SelectControl.new(disp_text, value, values, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_value(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.StringControl.new(disp_text, value, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_boolean(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.BooleanControl.new(disp_text, value, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_directory(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.DirectoryControl.new(disp_text, value, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
ctrl.dialog.title = disp_text
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_file(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.DirectoryControl.new(disp_text, value, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
ctrl.dialog.file_mode = ctrl.dialog.FILE_MODE_OPEN_FILE
|
||||
ctrl.dialog.title = disp_text
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_save_file_anywhere(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.DirectoryControl.new(disp_text, value, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
ctrl.dialog.file_mode = ctrl.dialog.FILE_MODE_SAVE_FILE
|
||||
ctrl.dialog.access = ctrl.dialog.ACCESS_FILESYSTEM
|
||||
ctrl.dialog.title = disp_text
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_color(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.ColorControl.new(disp_text, value, hint)
|
||||
_add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_save_load():
|
||||
var ctrl = PanelControls.SaveLoadControl.new('Config', '', '')
|
||||
_base_container.add_child(ctrl)
|
||||
|
||||
ctrl.save_path_chosen.connect(_on_save_path_chosen)
|
||||
ctrl.load_path_chosen.connect(_on_load_path_chosen)
|
||||
|
||||
_cfg_ctrls['save_load'] = ctrl
|
||||
return ctrl
|
||||
|
||||
# ------------------
|
||||
# Events
|
||||
# ------------------
|
||||
func _on_title_cell_draw(which):
|
||||
which.draw_rect(Rect2(Vector2(0, 0), which.size), Color(0, 0, 0, .15))
|
||||
|
||||
|
||||
func _on_save_path_chosen(path):
|
||||
save_file(path)
|
||||
|
||||
|
||||
func _on_load_path_chosen(path):
|
||||
load_file.bind(path).call_deferred()
|
||||
|
||||
# ------------------
|
||||
# Public
|
||||
# ------------------
|
||||
func get_config_issues():
|
||||
var to_return = []
|
||||
var has_directory = false
|
||||
|
||||
for i in range(DIRS_TO_LIST):
|
||||
var key = str('directory_', i)
|
||||
var path = _cfg_ctrls[key].value
|
||||
if(path != null and path != ''):
|
||||
has_directory = true
|
||||
if(!DirAccess.dir_exists_absolute(path)):
|
||||
_cfg_ctrls[key].mark_invalid(true)
|
||||
to_return.append(str('Test directory ', path, ' does not exist.'))
|
||||
else:
|
||||
_cfg_ctrls[key].mark_invalid(false)
|
||||
else:
|
||||
_cfg_ctrls[key].mark_invalid(false)
|
||||
|
||||
if(!has_directory):
|
||||
to_return.append('You do not have any directories set.')
|
||||
_titles.dirs.mark_invalid(true)
|
||||
else:
|
||||
_titles.dirs.mark_invalid(false)
|
||||
|
||||
if(!_cfg_ctrls.suffix.value.ends_with('.gd')):
|
||||
_cfg_ctrls.suffix.mark_invalid(true)
|
||||
to_return.append("Script suffix must end in '.gd'")
|
||||
else:
|
||||
_cfg_ctrls.suffix.mark_invalid(false)
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func clear():
|
||||
for key in _cfg_ctrls:
|
||||
_cfg_ctrls[key].free()
|
||||
|
||||
_cfg_ctrls.clear()
|
||||
|
||||
for entry in _all_titles:
|
||||
entry.free()
|
||||
|
||||
_all_titles.clear()
|
||||
|
||||
|
||||
func save_file(path):
|
||||
var gcfg = GutConfig.new()
|
||||
gcfg.options = get_options({})
|
||||
gcfg.save_file(path)
|
||||
|
||||
|
||||
|
||||
func load_file(path):
|
||||
var gcfg = GutConfig.new()
|
||||
gcfg.load_options(path)
|
||||
clear()
|
||||
set_options(gcfg.options)
|
||||
|
||||
|
||||
# --------------
|
||||
# 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(opts):
|
||||
var options = opts.duplicate()
|
||||
|
||||
# _add_title('Save/Load')
|
||||
_add_save_load()
|
||||
|
||||
_add_title("Settings")
|
||||
_add_number("log_level", options.log_level, "Log Level", 0, 3,
|
||||
"Detail level for log messages.\n" + \
|
||||
"\t0: Errors and failures only.\n" + \
|
||||
"\t1: Adds all test names + warnings + info\n" + \
|
||||
"\t2: Shows all asserts\n" + \
|
||||
"\t3: Adds more stuff probably, maybe not.")
|
||||
_add_boolean('ignore_pause', options.ignore_pause, 'Ignore Pause',
|
||||
"Ignore calls to pause_before_teardown")
|
||||
_add_boolean('hide_orphans', options.hide_orphans, 'Hide Orphans',
|
||||
'Do not display orphan counts in output.')
|
||||
_add_boolean('should_exit', options.should_exit, 'Exit on Finish',
|
||||
"Exit when tests finished.")
|
||||
_add_boolean('should_exit_on_success', options.should_exit_on_success, 'Exit on Success',
|
||||
"Exit if there are no failures. Does nothing if 'Exit on Finish' is enabled.")
|
||||
_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.value = 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('Runner Appearance')
|
||||
hide_this = _add_boolean("gut_on_top", options.gut_on_top, "On Top",
|
||||
"The GUT Runner appears above children added during tests.")
|
||||
_add_number('opacity', options.opacity, 'Opacity', 0, 100,
|
||||
"The opacity of GUT when tests are running.")
|
||||
hide_this = _add_boolean('should_maximize', options.should_maximize, 'Maximize',
|
||||
"Maximize GUT when tests are being run.")
|
||||
_add_boolean('compact_mode', options.compact_mode, 'Compact Mode',
|
||||
'The runner will be in compact mode. This overrides Maximize.')
|
||||
_add_select('font_name', options.font_name, GutUtils.avail_fonts, 'Font',
|
||||
"The font to use for text output in the Gut Runner.")
|
||||
_add_number('font_size', options.font_size, 'Font Size', 5, 100,
|
||||
"The font size for text output in the Gut Runner.")
|
||||
hide_this = _add_color('font_color', options.font_color, 'Font Color',
|
||||
"The font color for text output in the Gut Runner.")
|
||||
_add_color('background_color', options.background_color, 'Background Color',
|
||||
"The background color for text output in the Gut Runner.")
|
||||
_add_boolean('disable_colors', options.disable_colors, 'Disable Formatting',
|
||||
'Disable formatting and colors used in the Runner. Does not affect panel output.')
|
||||
|
||||
|
||||
_titles.dirs = _add_title('Test Directories')
|
||||
_add_boolean('include_subdirs', options.include_subdirs, 'Include Subdirs',
|
||||
"Include subdirectories of the directories configured below.")
|
||||
|
||||
var dirs_to_load = options.configured_dirs
|
||||
if(options.dirs.size() > dirs_to_load.size()):
|
||||
dirs_to_load = options.dirs
|
||||
|
||||
for i in range(DIRS_TO_LIST):
|
||||
var value = ''
|
||||
if(dirs_to_load.size() > i):
|
||||
value = dirs_to_load[i]
|
||||
|
||||
var test_dir = _add_directory(str('directory_', i), value, str(i))
|
||||
test_dir.enabled_button.visible = true
|
||||
test_dir.enabled_button.button_pressed = options.dirs.has(value)
|
||||
|
||||
|
||||
_add_title("XML Output")
|
||||
_add_save_file_anywhere("junit_xml_file", options.junit_xml_file, "Output Path",
|
||||
"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 " +
|
||||
"overriding the file use Include Timestamp.")
|
||||
_add_boolean("junit_xml_timestamp", options.junit_xml_timestamp, "Include Timestamp",
|
||||
"Include a timestamp in the filename so that each run gets its own xml file.")
|
||||
|
||||
|
||||
_add_title('Hooks')
|
||||
_add_file('pre_run_script', options.pre_run_script, 'Pre-Run Hook',
|
||||
'This script will be run by GUT before any tests are run.')
|
||||
_add_file('post_run_script', options.post_run_script, 'Post-Run Hook',
|
||||
'This script will be run by GUT after all tests are run.')
|
||||
|
||||
|
||||
_add_title('Misc')
|
||||
_add_value('prefix', options.prefix, 'Script Prefix',
|
||||
"The filename prefix for all test scripts.")
|
||||
_add_value('suffix', options.suffix, 'Script Suffix',
|
||||
"Script suffix, including .gd extension. For example '_foo.gd'.")
|
||||
_add_number('paint_after', options.paint_after, 'Paint After', 0.0, 1.0,
|
||||
"How long GUT will wait before pausing for 1 frame to paint the screen. 0 is never.")
|
||||
|
||||
# since _add_number doesn't set step property, it will default to a step of
|
||||
# 1 and cast values to int. Give it a .5 step and re-set the value.
|
||||
_cfg_ctrls.paint_after.value_ctrl.step = .05
|
||||
_cfg_ctrls.paint_after.value = options.paint_after
|
||||
|
||||
|
||||
|
||||
func get_options(base_opts):
|
||||
var to_return = base_opts.duplicate()
|
||||
|
||||
# Settings
|
||||
to_return.log_level = _cfg_ctrls.log_level.value
|
||||
to_return.ignore_pause = _cfg_ctrls.ignore_pause.value
|
||||
to_return.hide_orphans = _cfg_ctrls.hide_orphans.value
|
||||
to_return.should_exit = _cfg_ctrls.should_exit.value
|
||||
to_return.should_exit_on_success = _cfg_ctrls.should_exit_on_success.value
|
||||
to_return.double_strategy = _cfg_ctrls.double_strategy.value
|
||||
to_return.errors_do_not_cause_failure = !_cfg_ctrls.errors_cause_failure.value
|
||||
|
||||
|
||||
# Runner Appearance
|
||||
to_return.font_name = _cfg_ctrls.font_name.text
|
||||
to_return.font_size = _cfg_ctrls.font_size.value
|
||||
to_return.should_maximize = _cfg_ctrls.should_maximize.value
|
||||
to_return.compact_mode = _cfg_ctrls.compact_mode.value
|
||||
to_return.opacity = _cfg_ctrls.opacity.value
|
||||
to_return.background_color = _cfg_ctrls.background_color.value.to_html()
|
||||
to_return.font_color = _cfg_ctrls.font_color.value.to_html()
|
||||
to_return.disable_colors = _cfg_ctrls.disable_colors.value
|
||||
to_return.gut_on_top = _cfg_ctrls.gut_on_top.value
|
||||
to_return.paint_after = _cfg_ctrls.paint_after.value
|
||||
|
||||
|
||||
# Directories
|
||||
to_return.include_subdirs = _cfg_ctrls.include_subdirs.value
|
||||
var dirs = []
|
||||
var configured_dirs = []
|
||||
for i in range(DIRS_TO_LIST):
|
||||
var key = str('directory_', i)
|
||||
var ctrl = _cfg_ctrls[key]
|
||||
if(ctrl.value != '' and ctrl.value != null):
|
||||
configured_dirs.append(ctrl.value)
|
||||
if(ctrl.enabled_button.button_pressed):
|
||||
dirs.append(ctrl.value)
|
||||
to_return.dirs = dirs
|
||||
to_return.configured_dirs = configured_dirs
|
||||
|
||||
# XML Output
|
||||
to_return.junit_xml_file = _cfg_ctrls.junit_xml_file.value
|
||||
to_return.junit_xml_timestamp = _cfg_ctrls.junit_xml_timestamp.value
|
||||
|
||||
# Hooks
|
||||
to_return.pre_run_script = _cfg_ctrls.pre_run_script.value
|
||||
to_return.post_run_script = _cfg_ctrls.post_run_script.value
|
||||
|
||||
# Misc
|
||||
to_return.prefix = _cfg_ctrls.prefix.value
|
||||
to_return.suffix = _cfg_ctrls.suffix.value
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func mark_saved():
|
||||
for key in _cfg_ctrls:
|
||||
_cfg_ctrls[key].mark_unsaved(false)
|
||||
241
addons/gut/gui/gut_gui.gd
Normal file
241
addons/gut/gui/gut_gui.gd
Normal file
@@ -0,0 +1,241 @@
|
||||
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
|
||||
80
addons/gut/gui/gut_user_preferences.gd
Normal file
80
addons/gut/gui/gut_user_preferences.gd
Normal file
@@ -0,0 +1,80 @@
|
||||
class GutEditorPref:
|
||||
var gut_pref_prefix = 'gut/'
|
||||
var pname = '__not_set__'
|
||||
var default = null
|
||||
var value = '__not_set__'
|
||||
var _settings = null
|
||||
|
||||
func _init(n, d, s):
|
||||
pname = n
|
||||
default = d
|
||||
_settings = s
|
||||
load_it()
|
||||
|
||||
func _prefstr():
|
||||
var to_return = str(gut_pref_prefix, pname)
|
||||
return to_return
|
||||
|
||||
func save_it():
|
||||
_settings.set_setting(_prefstr(), value)
|
||||
|
||||
func load_it():
|
||||
if(_settings.has_setting(_prefstr())):
|
||||
value = _settings.get_setting(_prefstr())
|
||||
else:
|
||||
value = default
|
||||
|
||||
func erase():
|
||||
_settings.erase(_prefstr())
|
||||
|
||||
|
||||
const EMPTY = '-- NOT_SET --'
|
||||
|
||||
# -- Editor ONLY Settings --
|
||||
var output_font_name = null
|
||||
var output_font_size = null
|
||||
var hide_result_tree = null
|
||||
var hide_output_text = null
|
||||
var hide_settings = null
|
||||
var use_colors = null # ? might be output panel
|
||||
|
||||
# var shortcut_run_all = null
|
||||
# var shortcut_run_current_script = null
|
||||
# var shortcut_run_current_inner = null
|
||||
# var shortcut_run_current_test = null
|
||||
# var shortcut_panel_button = null
|
||||
|
||||
|
||||
func _init(editor_settings):
|
||||
output_font_name = GutEditorPref.new('output_font_name', 'CourierPrime', editor_settings)
|
||||
output_font_size = GutEditorPref.new('output_font_size', 30, editor_settings)
|
||||
hide_result_tree = GutEditorPref.new('hide_result_tree', false, editor_settings)
|
||||
hide_output_text = GutEditorPref.new('hide_output_text', false, editor_settings)
|
||||
hide_settings = GutEditorPref.new('hide_settings', false, editor_settings)
|
||||
use_colors = GutEditorPref.new('use_colors', true, editor_settings)
|
||||
|
||||
# shortcut_run_all = GutEditorPref.new('shortcut_run_all', EMPTY, editor_settings)
|
||||
# shortcut_run_current_script = GutEditorPref.new('shortcut_run_current_script', EMPTY, editor_settings)
|
||||
# shortcut_run_current_inner = GutEditorPref.new('shortcut_run_current_inner', EMPTY, editor_settings)
|
||||
# shortcut_run_current_test = GutEditorPref.new('shortcut_run_current_test', EMPTY, editor_settings)
|
||||
# shortcut_panel_button = GutEditorPref.new('shortcut_panel_button', EMPTY, editor_settings)
|
||||
|
||||
func save_it():
|
||||
for prop in get_property_list():
|
||||
var val = get(prop.name)
|
||||
if(val is GutEditorPref):
|
||||
val.save_it()
|
||||
|
||||
|
||||
func load_it():
|
||||
for prop in get_property_list():
|
||||
var val = get(prop.name)
|
||||
if(val is GutEditorPref):
|
||||
val.load_it()
|
||||
|
||||
|
||||
func erase_all():
|
||||
for prop in get_property_list():
|
||||
var val = get(prop.name)
|
||||
if(val is GutEditorPref):
|
||||
val.erase()
|
||||
441
addons/gut/gui/panel_controls.gd
Normal file
441
addons/gut/gui/panel_controls.gd
Normal file
@@ -0,0 +1,441 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class BaseGutPanelControl:
|
||||
extends HBoxContainer
|
||||
var label = Label.new()
|
||||
var _lbl_unsaved = Label.new()
|
||||
var _lbl_invalid = Label.new()
|
||||
|
||||
var value = null:
|
||||
get: return get_value()
|
||||
set(val): set_value(val)
|
||||
|
||||
signal changed
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
mouse_filter = MOUSE_FILTER_PASS
|
||||
|
||||
label.size_flags_horizontal = label.SIZE_EXPAND_FILL
|
||||
label.mouse_filter = label.MOUSE_FILTER_STOP
|
||||
add_child(label)
|
||||
|
||||
_lbl_unsaved.text = '*'
|
||||
_lbl_unsaved.visible = false
|
||||
add_child(_lbl_unsaved)
|
||||
|
||||
_lbl_invalid.text = '!'
|
||||
_lbl_invalid.visible = false
|
||||
add_child(_lbl_invalid)
|
||||
|
||||
label.text = title
|
||||
label.tooltip_text = hint
|
||||
|
||||
|
||||
func mark_unsaved(is_it=true):
|
||||
_lbl_unsaved.visible = is_it
|
||||
|
||||
|
||||
func mark_invalid(is_it):
|
||||
_lbl_invalid.visible = is_it
|
||||
|
||||
# -- Virtual --
|
||||
#
|
||||
# value_ctrl (all should declare the value_ctrl)
|
||||
#
|
||||
func set_value(value):
|
||||
pass
|
||||
|
||||
func get_value():
|
||||
pass
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class NumberControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = SpinBox.new()
|
||||
|
||||
func _init(title, val, v_min, v_max, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
value_ctrl.value = val
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.min_value = v_min
|
||||
value_ctrl.max_value = v_max
|
||||
value_ctrl.value_changed.connect(_on_value_changed)
|
||||
value_ctrl.select_all_on_focus = true
|
||||
add_child(value_ctrl)
|
||||
|
||||
func _on_value_changed(new_value):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.value
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.value = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class StringControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = LineEdit.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.text = val
|
||||
value_ctrl.text_changed.connect(_on_text_changed)
|
||||
value_ctrl.select_all_on_focus = true
|
||||
add_child(value_ctrl)
|
||||
|
||||
func _on_text_changed(new_value):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.text
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.text = val
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class BooleanControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = CheckBox.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
value_ctrl.button_pressed = val
|
||||
value_ctrl.toggled.connect(_on_button_toggled)
|
||||
add_child(value_ctrl)
|
||||
|
||||
func _on_button_toggled(new_value):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.button_pressed
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.button_pressed = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# value is "selected" and is gettable and settable
|
||||
# text is the text value of the selected item, it is gettable only
|
||||
# ------------------------------------------------------------------------------
|
||||
class SelectControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = OptionButton.new()
|
||||
|
||||
var text = '' :
|
||||
get: return value_ctrl.get_item_text(value_ctrl.selected)
|
||||
set(val): pass
|
||||
|
||||
func _init(title, val, choices, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
var select_idx = 0
|
||||
for i in range(choices.size()):
|
||||
value_ctrl.add_item(choices[i])
|
||||
if(val == choices[i]):
|
||||
select_idx = i
|
||||
value_ctrl.selected = select_idx
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.item_selected.connect(_on_item_selected)
|
||||
add_child(value_ctrl)
|
||||
|
||||
func _on_item_selected(idx):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.selected
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.selected = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class ColorControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = ColorPickerButton.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.color = val
|
||||
add_child(value_ctrl)
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.color
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.color = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class DirectoryControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl := LineEdit.new()
|
||||
var dialog := FileDialog.new()
|
||||
var enabled_button = CheckButton.new()
|
||||
|
||||
var _btn_dir := Button.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
label.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
_btn_dir.text = '...'
|
||||
_btn_dir.pressed.connect(_on_dir_button_pressed)
|
||||
|
||||
value_ctrl.text = val
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.select_all_on_focus = true
|
||||
value_ctrl.text_changed.connect(_on_value_changed)
|
||||
|
||||
dialog.file_mode = dialog.FILE_MODE_OPEN_DIR
|
||||
dialog.unresizable = false
|
||||
dialog.dir_selected.connect(_on_selected)
|
||||
dialog.file_selected.connect(_on_selected)
|
||||
|
||||
enabled_button.button_pressed = true
|
||||
enabled_button.visible = false
|
||||
|
||||
add_child(enabled_button)
|
||||
add_child(value_ctrl)
|
||||
add_child(_btn_dir)
|
||||
add_child(dialog)
|
||||
|
||||
func _update_display():
|
||||
var is_empty = value_ctrl.text == ''
|
||||
enabled_button.button_pressed = !is_empty
|
||||
enabled_button.disabled = is_empty
|
||||
|
||||
|
||||
func _ready():
|
||||
if(Engine.is_editor_hint()):
|
||||
dialog.size = Vector2(1000, 700)
|
||||
else:
|
||||
dialog.size = Vector2(500, 350)
|
||||
_update_display()
|
||||
|
||||
func _on_value_changed(new_text):
|
||||
_update_display()
|
||||
|
||||
func _on_selected(path):
|
||||
value_ctrl.text = path
|
||||
_update_display()
|
||||
|
||||
func _on_dir_button_pressed():
|
||||
dialog.current_dir = value_ctrl.text
|
||||
dialog.popup_centered()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.text
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.text = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Features:
|
||||
# Buttons to pick res://, user://, or anywhere on the OS.
|
||||
# ------------------------------------------------------------------------------
|
||||
class FileDialogSuperPlus:
|
||||
extends FileDialog
|
||||
|
||||
var show_diretory_types = true :
|
||||
set(val) :
|
||||
show_diretory_types = val
|
||||
_update_display()
|
||||
|
||||
var show_res = true :
|
||||
set(val) :
|
||||
show_res = val
|
||||
_update_display()
|
||||
|
||||
var show_user = true :
|
||||
set(val) :
|
||||
show_user = val
|
||||
_update_display()
|
||||
|
||||
var show_os = true :
|
||||
set(val) :
|
||||
show_os = val
|
||||
_update_display()
|
||||
|
||||
var _dir_type_hbox = null
|
||||
var _btn_res = null
|
||||
var _btn_user = null
|
||||
var _btn_os = null
|
||||
|
||||
func _ready():
|
||||
_init_controls()
|
||||
_update_display()
|
||||
|
||||
|
||||
func _init_controls():
|
||||
_dir_type_hbox = HBoxContainer.new()
|
||||
|
||||
_btn_res = Button.new()
|
||||
_btn_user = Button.new()
|
||||
_btn_os = Button.new()
|
||||
var spacer1 = CenterContainer.new()
|
||||
spacer1.size_flags_horizontal = spacer1.SIZE_EXPAND_FILL
|
||||
var spacer2 = spacer1.duplicate()
|
||||
|
||||
_dir_type_hbox.add_child(spacer1)
|
||||
_dir_type_hbox.add_child(_btn_res)
|
||||
_dir_type_hbox.add_child(_btn_user)
|
||||
_dir_type_hbox.add_child(_btn_os)
|
||||
_dir_type_hbox.add_child(spacer2)
|
||||
|
||||
_btn_res.text = 'res://'
|
||||
_btn_user.text = 'user://'
|
||||
_btn_os.text = ' OS '
|
||||
|
||||
get_vbox().add_child(_dir_type_hbox)
|
||||
get_vbox().move_child(_dir_type_hbox, 0)
|
||||
|
||||
_btn_res.pressed.connect(func(): access = ACCESS_RESOURCES)
|
||||
_btn_user.pressed.connect(func(): access = ACCESS_USERDATA)
|
||||
_btn_os.pressed.connect(func(): access = ACCESS_FILESYSTEM)
|
||||
|
||||
|
||||
func _update_display():
|
||||
if(is_inside_tree()):
|
||||
_dir_type_hbox.visible = show_diretory_types
|
||||
_btn_res.visible = show_res
|
||||
_btn_user.visible = show_user
|
||||
_btn_os.visible = show_os
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class SaveLoadControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var btn_load = Button.new()
|
||||
var btn_save = Button.new()
|
||||
|
||||
var dlg_load := FileDialogSuperPlus.new()
|
||||
var dlg_save := FileDialogSuperPlus.new()
|
||||
|
||||
signal save_path_chosen(path)
|
||||
signal load_path_chosen(path)
|
||||
|
||||
func _init(title, val, hint):
|
||||
super._init(title, val, hint)
|
||||
|
||||
btn_load.text = "Load"
|
||||
btn_load.custom_minimum_size.x = 100
|
||||
btn_load.pressed.connect(_on_load_pressed)
|
||||
add_child(btn_load)
|
||||
|
||||
btn_save.text = "Save As"
|
||||
btn_save.custom_minimum_size.x = 100
|
||||
btn_save.pressed.connect(_on_save_pressed)
|
||||
add_child(btn_save)
|
||||
|
||||
dlg_load.file_mode = dlg_load.FILE_MODE_OPEN_FILE
|
||||
dlg_load.unresizable = false
|
||||
dlg_load.dir_selected.connect(_on_load_selected)
|
||||
dlg_load.file_selected.connect(_on_load_selected)
|
||||
add_child(dlg_load)
|
||||
|
||||
dlg_save.file_mode = dlg_save.FILE_MODE_SAVE_FILE
|
||||
dlg_save.unresizable = false
|
||||
dlg_save.dir_selected.connect(_on_save_selected)
|
||||
dlg_save.file_selected.connect(_on_save_selected)
|
||||
add_child(dlg_save)
|
||||
|
||||
|
||||
func _ready():
|
||||
if(Engine.is_editor_hint()):
|
||||
dlg_load.size = Vector2(1000, 700)
|
||||
dlg_save.size = Vector2(1000, 700)
|
||||
else:
|
||||
dlg_load.size = Vector2(500, 350)
|
||||
dlg_save.size = Vector2(500, 350)
|
||||
|
||||
func _on_load_selected(path):
|
||||
load_path_chosen.emit(path)
|
||||
|
||||
func _on_save_selected(path):
|
||||
save_path_chosen.emit(path)
|
||||
|
||||
func _on_load_pressed():
|
||||
dlg_load.popup_centered()
|
||||
|
||||
func _on_save_pressed():
|
||||
dlg_save.popup_centered()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# This one was never used in gut_config_gui...but I put some work into it and
|
||||
# I'm a sucker for that kinda thing. Delete this when you get tired of looking
|
||||
# at it.
|
||||
# ------------------------------------------------------------------------------
|
||||
# class Vector2Ctrl:
|
||||
# extends VBoxContainer
|
||||
|
||||
# var value = Vector2(-1, -1) :
|
||||
# get:
|
||||
# return get_value()
|
||||
# set(val):
|
||||
# set_value(val)
|
||||
# var disabled = false :
|
||||
# get:
|
||||
# return get_disabled()
|
||||
# set(val):
|
||||
# set_disabled(val)
|
||||
# var x_spin = SpinBox.new()
|
||||
# var y_spin = SpinBox.new()
|
||||
|
||||
# func _init():
|
||||
# add_child(_make_one('x: ', x_spin))
|
||||
# add_child(_make_one('y: ', y_spin))
|
||||
|
||||
# func _make_one(txt, spinner):
|
||||
# var hbox = HBoxContainer.new()
|
||||
# var lbl = Label.new()
|
||||
# lbl.text = txt
|
||||
# hbox.add_child(lbl)
|
||||
# hbox.add_child(spinner)
|
||||
# spinner.min_value = -1
|
||||
# spinner.max_value = 10000
|
||||
# spinner.size_flags_horizontal = spinner.SIZE_EXPAND_FILL
|
||||
# return hbox
|
||||
|
||||
# func set_value(v):
|
||||
# if(v != null):
|
||||
# x_spin.value = v[0]
|
||||
# y_spin.value = v[1]
|
||||
|
||||
# # Returns array instead of vector2 b/c that is what is stored in
|
||||
# # in the dictionary and what is expected everywhere else.
|
||||
# func get_value():
|
||||
# return [x_spin.value, y_spin.value]
|
||||
|
||||
# func set_disabled(should):
|
||||
# get_parent().visible = !should
|
||||
# x_spin.visible = !should
|
||||
# y_spin.visible = !should
|
||||
|
||||
# func get_disabled():
|
||||
# pass
|
||||
BIN
addons/gut/gui/play.png
Normal file
BIN
addons/gut/gui/play.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 143 B |
34
addons/gut/gui/play.png.import
Normal file
34
addons/gut/gui/play.png.import
Normal file
@@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cr6tvdv0ve6cv"
|
||||
path="res://.godot/imported/play.png-5c90e88e8136487a183a099d67a7de24.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/gui/play.png"
|
||||
dest_files=["res://.godot/imported/play.png-5c90e88e8136487a183a099d67a7de24.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
|
||||
239
addons/gut/gui/script_text_editor_controls.gd
Normal file
239
addons/gut/gui/script_text_editor_controls.gd
Normal file
@@ -0,0 +1,239 @@
|
||||
# Holds weakrefs to a ScriptTextEditor and related children nodes
|
||||
# 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
|
||||
# the one we use...so we hold a ref to the CodeTextEditor too.
|
||||
class ScriptEditorControlRef:
|
||||
var _text_edit = null
|
||||
var _script_editor = null
|
||||
var _code_editor = null
|
||||
|
||||
func _init(script_edit):
|
||||
_script_editor = weakref(script_edit)
|
||||
_populate_controls()
|
||||
# print("_script_editor = ", script_edit, ' vis = ', is_visible())
|
||||
|
||||
|
||||
func _populate_controls():
|
||||
# who knows if the tree will change so get the first instance of each
|
||||
# type of control we care about. Chances are there won't be more than
|
||||
# one of these in the future, but their position in the tree may change.
|
||||
_code_editor = weakref(_get_first_child_named('CodeTextEditor', _script_editor.get_ref()))
|
||||
_text_edit = weakref(_get_first_child_named("CodeEdit", _code_editor.get_ref()))
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
func get_script_text_edit():
|
||||
return _script_editor.get_ref()
|
||||
|
||||
|
||||
func get_text_edit():
|
||||
# ScriptTextEditors that are loaded when the project is opened
|
||||
# do not have their children populated yet. So if we may have to
|
||||
# _populate_controls again if we don't have one.
|
||||
if(_text_edit == null):
|
||||
_populate_controls()
|
||||
return _text_edit.get_ref()
|
||||
|
||||
|
||||
func get_script_editor():
|
||||
return _script_editor
|
||||
|
||||
|
||||
func is_visible():
|
||||
var to_return = false
|
||||
if(_script_editor.get_ref()):
|
||||
to_return = _script_editor.get_ref().visible
|
||||
return to_return
|
||||
|
||||
# ##############################################################################
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
# Used to make searching for the controls easier and faster.
|
||||
var _script_editors_parent = null
|
||||
# reference the ScriptEditor instance
|
||||
var _script_editor = null
|
||||
# Array of ScriptEditorControlRef containing all the opened ScriptTextEditors
|
||||
# and related controls at the time of the last refresh.
|
||||
var _script_editor_controls = []
|
||||
|
||||
var _method_prefix = 'test_'
|
||||
var _inner_class_prefix = 'Test'
|
||||
|
||||
func _init(script_edit):
|
||||
_script_editor = script_edit
|
||||
refresh()
|
||||
|
||||
|
||||
func _is_script_editor(obj):
|
||||
return str(obj).find('<ScriptTextEditor') != -1
|
||||
|
||||
|
||||
# Find the first ScriptTextEditor and then get its parent. Done this way
|
||||
# because who knows if the parent object will change. This is somewhat
|
||||
# future proofed.
|
||||
func _find_script_editors_parent():
|
||||
var _first_editor = _get_first_child_of_type_name("ScriptTextEditor", _script_editor)
|
||||
if(_first_editor != null):
|
||||
_script_editors_parent = _first_editor.get_parent()
|
||||
|
||||
|
||||
func _populate_editors():
|
||||
if(_script_editors_parent == null):
|
||||
return
|
||||
|
||||
_script_editor_controls.clear()
|
||||
for child in _script_editors_parent.get_children():
|
||||
if(_is_script_editor(child)):
|
||||
var ref = ScriptEditorControlRef.new(child)
|
||||
_script_editor_controls.append(ref)
|
||||
|
||||
# Yes, this is the same as the one above but with a different name. This was
|
||||
# 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):
|
||||
if(parent_obj == null):
|
||||
# print('aborting search for ', obj_name, ' parent is null')
|
||||
return null
|
||||
|
||||
var kids = parent_obj.get_children()
|
||||
var index = 0
|
||||
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):
|
||||
var this_one = str(kids[index])
|
||||
# print(search_for, ' :: ', this_one)
|
||||
if(this_one.find(search_for) != -1):
|
||||
to_return = kids[index]
|
||||
else:
|
||||
to_return = _get_first_child_of_type_name(obj_name, kids[index])
|
||||
if(to_return == null):
|
||||
index += 1
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func _get_func_name_from_line(text):
|
||||
text = text.strip_edges()
|
||||
var left = text.split("(")[0]
|
||||
var func_name = left.split(" ")[1]
|
||||
return func_name
|
||||
|
||||
|
||||
func _get_class_name_from_line(text):
|
||||
text = text.strip_edges()
|
||||
var right = text.split(" ")[1]
|
||||
var the_name = right.rstrip(":")
|
||||
return the_name
|
||||
|
||||
func refresh():
|
||||
if(_script_editors_parent == null):
|
||||
_find_script_editors_parent()
|
||||
# print("script editors parent = ", _script_editors_parent)
|
||||
|
||||
if(_script_editors_parent != null):
|
||||
_populate_editors()
|
||||
|
||||
# print("script editor controls = ", _script_editor_controls)
|
||||
|
||||
|
||||
func get_current_text_edit():
|
||||
var cur_script_editor = null
|
||||
var idx = 0
|
||||
|
||||
while(idx < _script_editor_controls.size() and cur_script_editor == null):
|
||||
if(_script_editor_controls[idx].is_visible()):
|
||||
cur_script_editor = _script_editor_controls[idx]
|
||||
else:
|
||||
idx += 1
|
||||
|
||||
var to_return = null
|
||||
if(cur_script_editor != null):
|
||||
to_return = cur_script_editor.get_text_edit()
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func get_script_editor_controls():
|
||||
var to_return = []
|
||||
for ctrl_ref in _script_editor_controls:
|
||||
to_return.append(ctrl_ref.get_script_text_edit())
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func get_line_info():
|
||||
var editor = get_current_text_edit()
|
||||
if(editor == null):
|
||||
return
|
||||
|
||||
var info = {
|
||||
script = null,
|
||||
inner_class = null,
|
||||
test_method = null
|
||||
}
|
||||
|
||||
var line = editor.get_caret_line()
|
||||
var done_func = false
|
||||
var done_inner = false
|
||||
while(line > 0 and (!done_func or !done_inner)):
|
||||
if(editor.can_fold_line(line)):
|
||||
var text = editor.get_line(line)
|
||||
var strip_text = text.strip_edges(true, false) # only left
|
||||
|
||||
if(!done_func and strip_text.begins_with("func ")):
|
||||
var func_name = _get_func_name_from_line(text)
|
||||
if(func_name.begins_with(_method_prefix)):
|
||||
info.test_method = func_name
|
||||
done_func = true
|
||||
# If the func line is left justified then there won't be any
|
||||
# inner classes above it.
|
||||
if(strip_text == text):
|
||||
done_inner = true
|
||||
|
||||
if(!done_inner and strip_text.begins_with("class")):
|
||||
var inner_name = _get_class_name_from_line(text)
|
||||
if(inner_name.begins_with(_inner_class_prefix)):
|
||||
info.inner_class = inner_name
|
||||
done_inner = true
|
||||
# if we found an inner class then we are already past
|
||||
# any test the cursor could be in.
|
||||
done_func = true
|
||||
line -= 1
|
||||
|
||||
return info
|
||||
|
||||
|
||||
func get_method_prefix():
|
||||
return _method_prefix
|
||||
|
||||
|
||||
func set_method_prefix(method_prefix):
|
||||
_method_prefix = method_prefix
|
||||
|
||||
|
||||
func get_inner_class_prefix():
|
||||
return _inner_class_prefix
|
||||
|
||||
|
||||
func set_inner_class_prefix(inner_class_prefix):
|
||||
_inner_class_prefix = inner_class_prefix
|
||||
1213
addons/gut/gut.gd
Normal file
1213
addons/gut/gut.gd
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user