Skip to content

Commit

Permalink
update GUT
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbrain committed Dec 6, 2023
1 parent 8407805 commit 1ae17b1
Show file tree
Hide file tree
Showing 41 changed files with 2,263 additions and 1,269 deletions.
17 changes: 10 additions & 7 deletions demo/addons/gut/GutScene.gd
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ extends Node2D
# ##############################################################################
@onready var _normal_gui = $Normal
@onready var _compact_gui = $Compact

var gut = null :
set(val):
gut = val
Expand All @@ -23,17 +24,20 @@ func _ready():
_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)

Expand Down Expand Up @@ -71,13 +75,12 @@ func get_textbox():


func set_font_size(new_size):
return
var rtl = _normal_gui.get_textbox()
if(rtl.get('custom_fonts/normal_font') != null):
rtl.get('custom_fonts/bold_italics_font').size = new_size
rtl.get('custom_fonts/bold_font').size = new_size
rtl.get('custom_fonts/italics_font').size = new_size
rtl.get('custom_fonts/normal_font').size = new_size

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):
Expand Down
3 changes: 3 additions & 0 deletions demo/addons/gut/awaiter.gd
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ func _physics_process(delta):


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
Expand Down
213 changes: 213 additions & 0 deletions demo/addons/gut/collected_script.gd
Original file line number Diff line number Diff line change
@@ -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 changes: 115 additions & 0 deletions demo/addons/gut/collected_test.gd
Original file line number Diff line number Diff line change
@@ -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


3 changes: 2 additions & 1 deletion demo/addons/gut/double_templates/function_template.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{func_decleration}
__gutdbl.spy_on('{method_name}', {param_array})
{vararg_warning}__gutdbl.spy_on('{method_name}', {param_array})
if(__gutdbl.should_call_super('{method_name}', {param_array})):
return {super_call}
else:
return __gutdbl.get_stubbed_return('{method_name}', {param_array})

Loading

0 comments on commit 1ae17b1

Please sign in to comment.