chore: add GUT test framework
This commit is contained in:
89
addons/gut/scripts/create_singleton_list.gd
Normal file
89
addons/gut/scripts/create_singleton_list.gd
Normal file
@@ -0,0 +1,89 @@
|
||||
extends SceneTree
|
||||
# Used to generate:
|
||||
# - res://addons/gut/godot_singletons.gd
|
||||
# - List of singletons for Singleton-Doubling.md
|
||||
# The data is pulled from a text file which contains the list of singletons
|
||||
# copied from Godot's @GlobalScope page. It was easier to spend 5x the time
|
||||
# making this than it was to just manually edit this stuff for each Godot
|
||||
# release.
|
||||
const SINGLETON_LIST_PATH = "res://templates/singleton_list.txt"
|
||||
const GODOT_SINGLETONS_PATH = "res://addons/gut/godot_singletons.gd"
|
||||
|
||||
var blacklist = [
|
||||
# I don't think this can be doubled unless we are running in the editor.
|
||||
"EditorInterface",
|
||||
]
|
||||
|
||||
var name_replacements = {
|
||||
# On mac the class name is IPUnix, but the singleton is IP.
|
||||
'IPUnix':'IP',
|
||||
}
|
||||
|
||||
func _parse_singleton_list():
|
||||
var list_text = FileAccess.get_file_as_string(SINGLETON_LIST_PATH)
|
||||
var lines = list_text.split("\n")
|
||||
var singleton_names = []
|
||||
for line in lines:
|
||||
line = line.strip_edges()
|
||||
if(!line.begins_with("#") and line != '' and !singleton_names.has(line)):
|
||||
singleton_names.append(line)
|
||||
return singleton_names
|
||||
|
||||
|
||||
func _write_godot_singletons_script():
|
||||
var names = _parse_singleton_list()
|
||||
var singleton_text = ""
|
||||
for name in names:
|
||||
if(singleton_text != ''):
|
||||
singleton_text += ",\n\t"
|
||||
if(blacklist.has(name)):
|
||||
singleton_text += "# excluded: " + name
|
||||
else:
|
||||
singleton_text += name
|
||||
|
||||
var script_text = """
|
||||
# This file is auto-generated as part of the release process. GUT maintainers
|
||||
# should not change this file manually.
|
||||
static var class_ref = [
|
||||
{singletons}
|
||||
]
|
||||
static var names = []
|
||||
static func _static_init():
|
||||
for entry in class_ref:
|
||||
names.append(entry.get_class())
|
||||
"""
|
||||
script_text = script_text.format({"singletons":singleton_text})
|
||||
var file = FileAccess.open(GODOT_SINGLETONS_PATH, FileAccess.WRITE)
|
||||
file.store_string(script_text)
|
||||
file.close()
|
||||
|
||||
var Loaded = ResourceLoader.load(GODOT_SINGLETONS_PATH, "", ResourceLoader.CACHE_MODE_REPLACE)
|
||||
if(Loaded == null):
|
||||
push_error("Could not load " + GODOT_SINGLETONS_PATH)
|
||||
print(script_text)
|
||||
else:
|
||||
print(Loaded.class_ref)
|
||||
print("File created.")
|
||||
|
||||
|
||||
func _get_doc_link(sname : String):
|
||||
var base = &"https://docs.godotengine.org/en/stable/classes/class_"
|
||||
return str(base, sname.to_lower(), '.html')
|
||||
|
||||
|
||||
# Eligible Singletons list in Doubling-Singletons.md
|
||||
func _double_singletons_md_file_list():
|
||||
ResourceLoader.load(GODOT_SINGLETONS_PATH, "", ResourceLoader.CACHE_MODE_REPLACE)
|
||||
print("")
|
||||
var text = ""
|
||||
GutUtils.GodotSingletons.names.sort()
|
||||
for s in GutUtils.GodotSingletons.names:
|
||||
var n = name_replacements.get(s, s)
|
||||
text += str("* [", n, '](', _get_doc_link(n), ")\n")
|
||||
return text
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_write_godot_singletons_script()
|
||||
print(_double_singletons_md_file_list())
|
||||
quit()
|
||||
1
addons/gut/scripts/create_singleton_list.gd.uid
Normal file
1
addons/gut/scripts/create_singleton_list.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dvafofggelyh7
|
||||
8
addons/gut/scripts/global.gd
Normal file
8
addons/gut/scripts/global.gd
Normal file
@@ -0,0 +1,8 @@
|
||||
extends Node
|
||||
# ##############################################################################
|
||||
# This script is used by test_command_line_auto_load.gd to show how autoload
|
||||
# can be handled via the command line interface for gut. This is not autoloaded
|
||||
# or used by this project in any other way.
|
||||
# ##############################################################################
|
||||
func print_loaded():
|
||||
print("global scope script loaded")
|
||||
1
addons/gut/scripts/global.gd.uid
Normal file
1
addons/gut/scripts/global.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://blr8nb3vvr0sk
|
||||
169
addons/gut/scripts/main.gd
Normal file
169
addons/gut/scripts/main.gd
Normal file
@@ -0,0 +1,169 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# This is an example of using the GutControl (res://addons/gut/gui/GutContro.tscn)
|
||||
# to execute tests in a deployed game.
|
||||
#
|
||||
# Setup:
|
||||
# Create a scene.
|
||||
# Add a GutControl to your scene, name it GutControl.
|
||||
# Add this script to your scene.
|
||||
# Run it.
|
||||
# ------------------------------------------------------------------------------
|
||||
extends Node2D
|
||||
@onready var _gut_control = $GutControl
|
||||
|
||||
# Holds a reference to the current test script object being run. Set in
|
||||
# signal callbacks.
|
||||
var _current_script_object = null
|
||||
# Holds the name of the current test being run. Set in signal callbacks.
|
||||
var _current_test_name = null
|
||||
|
||||
|
||||
func _ready():
|
||||
simple_setup()
|
||||
# complex_setup()
|
||||
|
||||
|
||||
func simple_setup():
|
||||
# You must load a gut config file to use this control. Here we are loading
|
||||
# the default config file used by the command line. You can use any config
|
||||
# file you have created. Use the "save as" button in the Settings subpanel
|
||||
# to create a config file, or write your own.
|
||||
#
|
||||
# Some settings may not work. For example, the exit flags do not have any
|
||||
# effect.
|
||||
#
|
||||
# Settings are not saved, so any changes will be lost. The idea is that you
|
||||
# want to deploy the settings and users should not be able to save them. If
|
||||
# you want to save changes, you can call:
|
||||
# _gut_control.get_config().write_options(path).
|
||||
# Note that you cannot to write to res:// on mobile platforms, so you will
|
||||
# have to juggle the initial loading from res:// or user:// and save to
|
||||
# user://.
|
||||
_gut_control.load_config_file('res://.gutconfig.json')
|
||||
|
||||
# That's it. Just get a reference to the control you added to the scene and
|
||||
# give it a config. The rest of the stuff in this script optional.
|
||||
|
||||
|
||||
|
||||
func complex_setup():
|
||||
# See simple setup
|
||||
_gut_control.load_config_file('res://.gutconfig.json')
|
||||
|
||||
# Returns a gut_config.gd instance.
|
||||
var config = _gut_control.get_config()
|
||||
|
||||
# Override specific values for the purposes of this scene. You can see all
|
||||
# the options available in the default_options dictionary in gut_config.gd.
|
||||
# Changing settings AFTER _ready will not have any effect.
|
||||
config.options.should_exit = false
|
||||
config.options.should_exit_on_success = false
|
||||
config.options.compact_mode = false
|
||||
# Note that if you are exporting xml results you may want to set the path to
|
||||
# a file in user:// instead of an absolute path. Your game may not have
|
||||
# permissions to save files elsewhere when running on a mobile device.
|
||||
config.options.junit_xml_file = 'user://deployed_results.xml'
|
||||
|
||||
# Some actions cannot be done until after _ready has finished in all objects
|
||||
_post_ready_setup.call_deferred()
|
||||
|
||||
|
||||
|
||||
|
||||
# If you would like to connect to signals provided by gut.gd then you must do
|
||||
# so after _ready. This is an example of getting a reference to gut and all
|
||||
# of the signals it provides.
|
||||
func _post_ready_setup():
|
||||
var gut = _gut_control.get_gut()
|
||||
gut.start_run.connect(_on_gut_run_start)
|
||||
|
||||
gut.start_script.connect(_on_gut_start_script)
|
||||
gut.end_script.connect(_on_gut_end_script)
|
||||
|
||||
gut.start_test.connect(_on_gut_start_test)
|
||||
gut.end_test.connect(_on_gut_end_test)
|
||||
|
||||
gut.end_run.connect(_on_gut_run_end)
|
||||
|
||||
|
||||
# -----------------------
|
||||
# Events
|
||||
# -----------------------
|
||||
func _on_gut_run_start():
|
||||
print('Starting tests')
|
||||
|
||||
|
||||
# This signal passes a TestCollector.gd/TestScript instance
|
||||
func _on_gut_start_script(script_obj):
|
||||
print(script_obj.get_full_name(), ' has ', script_obj.tests.size(), ' tests')
|
||||
_current_script_object = script_obj
|
||||
|
||||
|
||||
func _on_gut_end_script():
|
||||
var pass_count = 0
|
||||
for test in _current_script_object.tests:
|
||||
if(test.did_pass()):
|
||||
pass_count += 1
|
||||
print(pass_count, '/', _current_script_object.tests.size(), " passed\n")
|
||||
_current_script_object = null
|
||||
|
||||
|
||||
func _on_gut_start_test(test_name):
|
||||
_current_test_name = test_name
|
||||
print(' ', test_name)
|
||||
|
||||
|
||||
func _on_gut_end_test():
|
||||
# get_test_named returns a TestCollector.gd/Test instance for the name
|
||||
# passed in.
|
||||
var test_object = _current_script_object.get_test_named(_current_test_name)
|
||||
var status = "failed"
|
||||
if(test_object.did_pass()):
|
||||
status = "passed"
|
||||
elif(test_object.pending):
|
||||
status = "pending"
|
||||
|
||||
print(' ', status)
|
||||
_current_test_name = null
|
||||
|
||||
|
||||
func _on_gut_run_end():
|
||||
print('Tests Done')
|
||||
|
||||
|
||||
# You can kick of the tests via code if you want.
|
||||
func _on_run_gut_tests_button_pressed():
|
||||
_gut_control.run_tests()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
# The MIT License (MIT)
|
||||
# =====================
|
||||
#
|
||||
# Copyright (c) 2025 Tom "Butch" Wesley
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# ##############################################################################
|
||||
1
addons/gut/scripts/main.gd.uid
Normal file
1
addons/gut/scripts/main.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c6wm4h7xfrlor
|
||||
Reference in New Issue
Block a user