chore: add GUT test framework

This commit is contained in:
xiaji
2026-05-29 09:16:10 +08:00
parent 5741ba1dc0
commit 07fc763413
808 changed files with 76903 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
extends GutInternalTester
@abstract
class AbstractClass:
@abstract func abstract_method()
@abstract
class ExtendsAbstractClass:
extends AbstractClass
func abstract_method():
return "implemented"
@abstract
func another_abstract_method()
func before_all():
register_inner_classes(get_script())
# Our implementaiton assumes that the order of get_script_method_list() is
# from child to parents. The following test will show when future godot
# versions change the behavior.
func test_method_order_assumption():
var method_list = (ExtendsAbstractClass as Variant).get_script_method_list()
assert_eq(method_list[0].name, "abstract_method")
assert_eq(method_list[0].flags, 1)
assert_eq(method_list[1].name, "another_abstract_method")
assert_eq(method_list[2].name, "abstract_method")
assert_eq(method_list[2].flags, 129)
func test_can_double_abstract():
var dbl = double(AbstractClass)
assert_not_null(dbl)
func test_can_stub_to_return_for_abstract_method_at_sctipt_level():
stub(AbstractClass, 'abstract_method').to_return('a')
var inst = double(AbstractClass).new()
assert_eq(inst.abstract_method(), 'a')
func test_can_stub_to_return_for_abstract_method_at_double_level():
var Dbl = double(AbstractClass)
stub(Dbl, 'abstract_method').to_return(9)
var inst = Dbl.new()
assert_eq(inst.abstract_method(), 9)
func test_can_stub_to_return_for_abstract_method_at_instance_level():
var inst = double(AbstractClass).new()
stub(inst.abstract_method).to_return(7)
assert_eq(inst.abstract_method(), 7)
func test_error_when_stubbing_to_call_super_at_script_level():
stub(AbstractClass, 'abstract_method').to_call_super()
assert_tracked_gut_error(gut, 0)
func test_error_when_stubbing_to_call_super_at_instance_level():
# Arrange
var doubled = autofree(double(AbstractClass).new())
stub(doubled.abstract_method).to_call_super()
# Act
var result = doubled.abstract_method()
# Assert
assert_null(result)
var current_test_errors = gut.error_tracker.get_current_test_errors()
assert_eq(current_test_errors[0].code, "Cannot call super() because method abstract_method is abstract.")
assert_tracked_gut_error()
func test_can_stub_implemented_abstract_to_call_super():
var inst = double(ExtendsAbstractClass).new()
stub(inst.abstract_method).to_call_super()
assert_eq(inst.abstract_method(), 'implemented')

View File

@@ -0,0 +1 @@
uid://ckw6xee3xj3e3

View File

@@ -0,0 +1,74 @@
extends GutInternalTester
class TestBoth:
extends GutInternalTester
const INIT_PARAMETERS = 'res://test/resources/stub_test_objects/init_parameters.gd'
var InitParameters = load(INIT_PARAMETERS)
var _spy = null
var _doubler = null
func before_each():
_spy = GutUtils.Spy.new()
_doubler = GutUtils.Doubler.new()
_doubler.set_spy(_spy)
# var stubber = GutUtils.Stubber.new()
# _doubler.set_stubber(stubber)
func after_each():
_spy.clear()
func test_spy_is_set_in_metadata():
var inst = autofree(_doubler.double(DoubleMe).new())
assert_eq(inst.__gutdbl.spy_ref.get_ref(), _spy)
func test_when_doubled_method_called_spy_sees_it():
var inst = autofree(_doubler.double(DoubleMe).new())
inst.set_value(5)
assert_true(_spy.was_called(inst, 'set_value'))
func test_when_doubled_method_called_it_sends_parameters():
var inst = autofree(_doubler.double(DoubleMe).new())
inst.set_value(5)
assert_true(_spy.was_called(inst, 'set_value', [5]))
func test_it_works_with_two_parameters_too():
var inst = autofree(_doubler.double(DoubleMe).new())
inst.has_two_params_one_default('a', 'b')
assert_false(_spy.was_called(inst, 'has_two_params_one_default', ['c', 'd']), 'should not match')
assert_true(_spy.was_called(inst, 'has_two_params_one_default', ['a', 'b']), 'should match')
func test_can_spy_on_built_ins_when_doing_a_full_double():
_doubler.set_strategy(DOUBLE_STRATEGY.INCLUDE_NATIVE)
var inst = autofree(_doubler.double(DoubleMe).new())
# add_user_signal is a function on Object that isn't in our subclass.
inst.add_user_signal('new_signal', [])
inst.add_user_signal('signal_with_params', ['a', 'b'])
assert_true(_spy.was_called(inst, 'add_user_signal'), 'added first signal')
assert_true(_spy.was_called(inst, 'add_user_signal', ['signal_with_params', ['a', 'b']]), 'second signal added')
func test_can_spy_on_native_doubles():
var inst = autofree(_doubler.partial_double_gdnative(Node2D).new())
inst.set_position(Vector2(20, 20))
assert_true(_spy.was_called(inst, 'set_position'))
assert_true(_spy.was_called(inst, 'set_position', [Vector2(20, 20)]))
func test_can_spy_on_singleton_doubles():
var inst = _doubler.double_singleton(Input).new()
inst.is_action_just_pressed("foobar")
assert_true(_spy.was_called(inst, "is_action_just_pressed"))
func test_can_spy_on_singleton_parameters():
var inst = _doubler.double_singleton(OS).new()
inst.is_process_running(5)
assert_true(_spy.was_called(inst, 'is_process_running', [5]))
func test_can_spy_on_init():
var inst = _doubler.double(InitParameters).new('test_value')
assert_true(_spy.was_called(inst, '_init'))
func test_can_spy_on_init_parameters():
var inst = _doubler.double(InitParameters).new('test_value')
assert_true(_spy.was_called(inst, '_init', ['test_value']))

View File

@@ -0,0 +1 @@
uid://cule2ck72y45b

View File

@@ -0,0 +1,380 @@
extends "res://addons/gut/test.gd"
class BaseTest:
extends GutTest
var Stubber = load('res://addons/gut/stubber.gd')
var Doubler = load('res://addons/gut/doubler.gd')
var StubParams = load('res://addons/gut/stub_params.gd')
var Wm = GutUtils.WarningsManager
const DOUBLE_ME_PATH = 'res://test/resources/doubler_test_objects/double_me.gd'
const DOUBLE_ME_SCENE_PATH = 'res://test/resources/doubler_test_objects/double_me_scene.tscn'
const DOUBLE_EXTENDS_NODE2D = 'res://test/resources/doubler_test_objects/double_extends_node2d.gd'
const DOUBLE_EXTENDS_WINDOW_DIALOG = 'res://test/resources/doubler_test_objects/double_extends_window_dialog.gd'
const TO_STUB_PATH = 'res://test/resources/stub_test_objects/to_stub.gd'
const DOUBLE_WITH_STATIC = 'res://test/resources/doubler_test_objects/has_static_method.gd'
const INIT_PARAMETERS = 'res://test/resources/stub_test_objects/init_parameters.gd'
const INNER_CLASSES_PATH = 'res://test/resources/doubler_test_objects/inner_classes.gd'
const DOUBLE_DEFAULT_PARAMETERS = 'res://test/resources/doubler_test_objects/double_default_parameters.gd'
var DoubleMe = Wm.load_script_ignoring_all_warnings(DOUBLE_ME_PATH)
var DoubleExtendsNode2D = Wm.load_script_ignoring_all_warnings(DOUBLE_EXTENDS_NODE2D)
var DoubleExtendsWindowDialog = Wm.load_script_ignoring_all_warnings(DOUBLE_EXTENDS_WINDOW_DIALOG)
var DoubleWithStatic = Wm.load_script_ignoring_all_warnings(DOUBLE_WITH_STATIC)
var DoubleMeScene = Wm.load_script_ignoring_all_warnings(DOUBLE_ME_SCENE_PATH)
var InnerClasses = Wm.load_script_ignoring_all_warnings(INNER_CLASSES_PATH)
var DoubleDefaultParameters = Wm.load_script_ignoring_all_warnings(DOUBLE_DEFAULT_PARAMETERS)
var InitParameters = Wm.load_script_ignoring_all_warnings(INIT_PARAMETERS)
class TestTheBasics:
extends BaseTest
var gr = {
doubler = null,
stubber = null
}
func before_each():
gr.doubler = Doubler.new()
gr.stubber = Stubber.new()
gr.doubler.set_stubber(gr.stubber)
gr.doubler.add_ignored_method(DoubleMe, '_notification')
func test_stubbing_method_returns_expected_value():
var D = gr.doubler.double(DoubleMe)
var sp = StubParams.new(DOUBLE_ME_PATH, 'get_value').to_return(7)
gr.stubber.add_stub(sp)
var dbl = autofree(D.new())
assert_is(dbl, DoubleMe, 'it is a DoubleMe')
assert_eq(dbl.get_value(), 7)
if(is_failing()):
print(DoubleMe, D, dbl)
# print(gr.stubber.to_s())
func test_can_stub_non_local_methods():
var D = autofree(gr.doubler.double(DoubleMe))
var sp = StubParams.new(DOUBLE_ME_PATH, 'get_position').to_return(Vector2(11, 11))
gr.stubber.add_stub(sp)
assert_eq(autofree(D.new()).get_position(), Vector2(11, 11))
func test_when_non_local_methods_not_stubbed_super_is_returned():
var D = autofree(gr.doubler.double(DoubleMe))
var d = autofree(D.new())
assert_eq(d.get_child_count(), 0)
func test_can_stub_doubled_instance_values():
var D = autofree(gr.doubler.double(DoubleMe))
var d1 = autofree(D.new())
var d2 = autofree(D.new())
var sp1 = StubParams.new(DOUBLE_ME_PATH, 'get_value').to_return(5)
gr.stubber.add_stub(sp1)
var sp2 = StubParams.new(d1, 'get_value').to_return(10)
gr.stubber.add_stub(sp2)
assert_eq(d1.get_value(), 10, 'instantiate gets right value')
assert_eq(d2.get_value(), 5, 'other instantiate gets class value')
func test_stubbed_methods_send_parameters_in_callback():
var sp = StubParams.new(DOUBLE_ME_PATH, 'has_one_param')
sp.to_return(10).when_passed(1)
gr.stubber.add_stub(sp)
var d = autofree(gr.doubler.double(DoubleMe).new())
assert_eq(d.has_one_param(1), 10)
assert_eq(d.has_one_param('asdf'), null)
func test_stub_with_nothing_works_with_parameters():
var sp1 = StubParams.new(DOUBLE_ME_PATH, 'has_one_param').to_return(5)
var sp2 = StubParams.new(DOUBLE_ME_PATH, 'has_one_param')
sp2.to_return(10).when_passed(1)
gr.stubber.add_stub(sp1)
gr.stubber.add_stub(sp2)
var d = autofree(gr.doubler.double(DoubleMe).new())
assert_eq(d.has_one_param(), 5)
if(is_failing()):
print(gr.stubber.to_s())
func test_can_stub_doubled_scenes():
var sp = StubParams.new(DOUBLE_ME_SCENE_PATH, 'return_hello')
sp.to_return('world')
gr.stubber.add_stub(sp)
var inst = autofree(gr.doubler.double_scene(DoubleMeScene).instantiate())
assert_eq(inst.return_hello(), 'world')
func test_when_stubbed_to_call_super_then_super_is_called():
var doubled = autofree(gr.doubler.double(DoubleMe).new())
var params = GutUtils.StubParams.new(doubled, 'set_value').to_call_super()
gr.stubber.add_stub(params)
doubled.set_value(99)
assert_eq(doubled._value, 99)
func test_when_super_awaits_the_method_awaits():
var doubled = add_child_autofree(gr.doubler.double(DoubleMe).new())
var params = GutUtils.StubParams.new(doubled.await_seconds).to_call_super()
gr.stubber.add_stub(params)
var before = Time.get_ticks_msec()
await doubled.await_seconds(1)
var elapsed = Time.get_ticks_msec() - before
assert_almost_eq(elapsed, 1000, 300) # 300 seems like a lot, but i guess it's not.
func test_can_stub_native_methods():
var d_node2d = autofree(gr.doubler.double_gdnative(Node2D).new())
var params = GutUtils.StubParams.new(d_node2d, 'get_position').to_return(-1)
gr.stubber.add_stub(params)
assert_eq(d_node2d.get_position(), -1)
func test_partial_double_of_Node2D_returns_super_values():
var pd_node_2d = autofree(gr.doubler.partial_double_gdnative(Node2D).new())
assert_eq(pd_node_2d.is_blocking_signals(), false)
func test_can_stub_all_Node2D_doubles():
var d_node2d = autofree(gr.doubler.double_gdnative(Node2D).new())
var params = GutUtils.StubParams.new(Node2D, 'get_position').to_return(-1)
gr.stubber.add_stub(params)
assert_eq(d_node2d.get_position(), -1)
if(is_failing()):
print("Node2D = ", Node2D)
print(gr.stubber.to_s())
func test_double_can_have_default_param_values_stubbed():
var params = GutUtils.StubParams.new(INIT_PARAMETERS, '_init')
params.param_defaults(["override_default"])
gr.stubber.add_stub(params)
var inst = gr.doubler.double(InitParameters).new()
assert_eq(inst.value, 'override_default')
if(is_failing()):
print(gr.stubber.to_s())
func test_double_can_have_default_param_values_stubbed_after_double_created():
var Dbl = gr.doubler.double(InitParameters)
var params = GutUtils.StubParams.new(INIT_PARAMETERS, '_init')
params.param_defaults(["override_default"])
gr.stubber.add_stub(params)
var inst = Dbl.new()
assert_eq(inst.value, 'override_default')
if(is_failing()):
print(gr.stubber.to_s())
class TestInnerClasses:
extends BaseTest
var doubler = null
var stubber = null
func before_each():
doubler = Doubler.new()
stubber = GutUtils.Stubber.new()
doubler.set_stubber(stubber)
func test_can_stub_inner_using_loaded_inner_class():
doubler.inner_class_registry.register(InnerClasses)
var sp = StubParams.new(InnerClasses.InnerA, 'get_a').to_return(5)
stubber.add_stub(sp)
var dbl_inner_a = doubler.double(InnerClasses.InnerA).new()
assert_eq(stubber.get_return(dbl_inner_a, 'get_a'), 5)
# Since defaults are only available for built-in methods these tests verify
# specific method parameters that were found to cause a problem.
class TestDefaultParameters:
extends BaseTest
var doubler = null
var stubber = null
func before_all():
register_inner_classes(get_script())
func before_each():
doubler = Doubler.new(GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE)
stubber = GutUtils.Stubber.new()
doubler.set_stubber(stubber)
func test_default_values_are_set_in_stubber():
var dbl = autofree(doubler.double(DoubleDefaultParameters).new())
var default_value = stubber.get_default_value(DoubleDefaultParameters, 'default_string', 0)
assert_eq(default_value, 's')
if(is_failing()):
print(stubber.to_s())
func test_partial_gets_deault_values():
var dbl = autofree(doubler.partial_double(DoubleDefaultParameters).new())
var result = dbl.return_passed()
assert_eq(result, 'ab', 'the defauts are a and b')
func test_partial_gets_passed_values_and_defaults():
var dbl = autofree(doubler.partial_double(DoubleDefaultParameters).new())
var result = dbl.return_passed('foo')
assert_eq(result, 'foob')
func test_partial_gets_all_values_passed():
var dbl = autofree(doubler.partial_double(DoubleDefaultParameters).new())
var result = dbl.return_passed('foo', 'bar')
assert_eq(result, 'foobar')
func test_default_parameters_for_double_when_stubbed():
var dbl = autofree(doubler.double(DoubleDefaultParameters, DOUBLE_STRATEGY.SCRIPT_ONLY).new())
var params = GutUtils.StubParams.new(
dbl.defaulted_second_parameter.bind("jump"))\
.to_return('hello')
stubber.add_stub(params)
assert_eq(stubber.get_default_value(dbl, 'defaulted_second_parameter', 0), null)
assert_eq(stubber.get_default_value(dbl, 'defaulted_second_parameter', 1), 'p2')
class TestMonkeyPatching:
extends BaseTest
var doubler = null
var stubber = null
var call_this_value = null
func call_this(value):
call_this_value = value
var return_this_value = 99
func return_this():
return return_this_value
func return_passed(p1=null, p2=null, p3=null, p4=null, p5=null, p6=null):
return [p1, p2, p3, p4, p5, p6]
func before_each():
call_this_value = null
doubler = Doubler.new()
stubber = GutUtils.Stubber.new()
doubler.set_stubber(stubber)
func test_stubbed_method_calls_method():
var dbl = autofree(doubler.double(DoubleMe).new())
var params = GutUtils.StubParams.new(dbl.set_value).to_call(call_this)
stubber.add_stub(params)
dbl.set_value(9)
assert_eq(call_this_value, 9)
func test_stubbed_method_returns_value():
var dbl = autofree(doubler.double(DoubleMe).new())
var params = GutUtils.StubParams.new(dbl.get_value).to_call(return_this)
stubber.add_stub(params)
var result = dbl.get_value()
assert_eq(result, 99)
func test_with_bound_parameters():
var dbl = autofree(doubler.double(DoubleMe).new())
var params = GutUtils.StubParams.new(dbl.has_two_params_one_default)
params.to_call(return_passed.bind('three', 'four'))
stubber.add_stub(params)
var result = dbl.has_two_params_one_default('one', 'two')
assert_eq(result, ["one", "two", "three", "four", null, null])
func test_with_lambda_that_awaits():
var lambda = func(p1, _p2=null):
await get_tree().create_timer(p1).timeout
var dbl = autofree(doubler.double(DoubleMe).new())
var params = GutUtils.StubParams.new(dbl.has_two_params_one_default)
params.to_call(lambda)
stubber.add_stub(params)
var before = Time.get_ticks_msec()
await dbl.has_two_params_one_default(1, 2)
var elapsed = Time.get_ticks_msec() - before
assert_almost_eq(elapsed, 1000, 300) # yea, 300 is what you need.
class TestSingletons:
extends GutInternalTester
var doubler = null
var stubber = null
func before_each():
doubler = GutUtils.Doubler.new()
stubber = GutUtils.Stubber.new()
doubler.set_stubber(stubber)
func test_can_stub_singleton_method_via_class_to_return_value():
var params = GutUtils.StubParams.new(Time.get_ticks_msec).to_return(99)
stubber.add_stub(params)
var dbl = doubler.double_singleton(Time).new()
assert_eq(dbl.get_ticks_msec(), 99)
func test_can_stub_singleton_double_to_return_value():
var dbl = doubler.double_singleton(Time).new()
var params = GutUtils.StubParams.new(dbl.get_ticks_msec).to_return(10)
stubber.add_stub(params)
assert_eq(dbl.get_ticks_msec(), 10)
func test_can_stub_method_to_return_based_on_parameters():
var params = GutUtils.StubParams.new(OS.get_system_dir)\
.to_return("/asdf")\
.when_passed('foo', true)
stubber.add_stub(params)
var dbl = doubler.double_singleton(OS).new()
assert_eq(dbl.get_system_dir('bar', true), null)
assert_eq(dbl.get_system_dir('foo', true), '/asdf')
func test_can_stub_method_to_return_based_on_parameters_without_specifying_defaults():
var params = GutUtils.StubParams.new(OS.get_system_dir)\
.to_return("/asdf")\
.when_passed('foo', true)
stubber.add_stub(params)
var dbl = doubler.double_singleton(OS).new()
assert_eq(dbl.get_system_dir('bar'), null)
assert_eq(dbl.get_system_dir('foo'), '/asdf')
func test_can_stub_singleton_to_call_a_func():
var params = GutUtils.StubParams.new(OS.get_system_dir)\
.to_call(func(a, b): return '/hello')\
.when_passed('foo', true)
stubber.add_stub(params)
var dbl = doubler.double_singleton(OS).new()
assert_eq(dbl.get_system_dir('foo'), '/hello')
func test_can_stub_method_to_use_singleton_method():
var dbl = doubler.double_singleton(Time).new()
var params = GutUtils.StubParams.new(dbl.get_date_dict_from_unix_time)\
.to_use_singleton()
stubber.add_stub(params)
assert_eq(dbl.get_date_dict_from_unix_time(10), Time.get_date_dict_from_unix_time(10))
func test_default_method_paramters_for_input_singleton():
var dbl = doubler.double_singleton(Input).new()
var method = 'is_action_just_pressed'
assert_eq(stubber.get_default_value(dbl, method, 0), null)
assert_eq(stubber.get_default_value(dbl, method, 1), false)
# if(is_failing()):
# print(stubber.to_s())
func test_default_method_paramters_for_input_singleton_when_stubbed():
var dbl = doubler.double_singleton(Input).new()
var method = 'is_action_just_pressed'
var params = GutUtils.StubParams.new(
dbl.is_action_just_pressed.bind("jump"))\
.to_return(true)
stubber.add_stub(params)
assert_eq(stubber.get_default_value(dbl, method, 0), null)
assert_eq(stubber.get_default_value(dbl, method, 1), false)

View File

@@ -0,0 +1 @@
uid://csj3khcpdpx1k

View File

@@ -0,0 +1,61 @@
extends GutInternalTester
class TestLogging:
extends GutInternalTester
var _gut = null
func before_each():
_gut = new_gut(verbose)
_gut._should_print_versions = false
_gut.log_level = 0
add_child_autofree(_gut)
func test_gut_sets_doublers_logger():
assert_eq(_gut.get_doubler().get_logger(), _gut.logger, 'Doubler logger')
assert_eq(_gut.get_doubler()._method_maker.get_logger(), _gut.logger, 'MethodMaker logger')
func test_gut_sets_stubber_logger():
assert_eq(_gut.get_stubber().get_logger(), _gut.logger)
# This test makes assertion using THIS test script instance since it would
# be super hard to get a test object that was being run.
func test_gut_sets_logger_on_tests():
assert_eq(gut.logger, get_logger())
func test_gut_sets_logger_on_test_collector():
assert_eq(_gut._test_collector.get_logger(), _gut.logger)
func test_gut_sets_logger_on_spy():
assert_eq(_gut.get_spy().get_logger(), _gut.logger)
func test_method_maker_has_same_logger():
var mm = _gut.get_doubler()._method_maker
assert_eq(mm.get_logger(), _gut.logger)
func test_test_colledtor_has_same_logger():
assert_eq(_gut.get_test_collector().get_logger(), _gut.logger)
class TestMemoryMgmt:
extends GutTest
func after_each():
assert_no_new_orphans()
func test_GutTest():
var t = GutTest.new()
add_child(t)
t.free()
assert_no_new_orphans()
func test_GutTest_with_waits():
var t = GutTest.new()
add_child(t)
await wait_physics_frames(10)
t.free()
await wait_physics_frames(10)
assert_no_new_orphans()

View File

@@ -0,0 +1 @@
uid://c8puh05o6ilmp

View File

@@ -0,0 +1,506 @@
extends GutTest
class TestDefaults:
extends GutInternalTester
func test_gut_uses_utils_error_tracker_by_default():
var g = autofree(Gut.new(GutLogger.new()))
assert_eq(g.error_tracker, GutUtils.get_error_tracker())
class TestErrorFailures:
extends GutInternalTester
func should_skip_script():
return skip_if_debugger_active()
var _gut = null
func before_all():
gut.error_tracker.disabled = true
verbose = false
DynamicGutTest.should_print_source = verbose
func before_each():
_gut = add_child_autofree(new_gut(verbose))
GutErrorTracker.register_logger(_gut.error_tracker)
func after_each():
GutErrorTracker.deregister_logger(_gut.error_tracker)
func after_all():
gut.error_tracker.disabled = false
var _src_push_error = """
func test_with_push_error():
push_error('pushed error')
assert_true(true, 'passing assert')
"""
var _src_gut_error = """
func test_with_gut_error():
_lgr.error('this is a gut error')
assert_true(true, 'passing assert')
"""
var _src_script_error_in_called_method = """
func divide_them(a, b):
return a / b
func test_with_script_error():
divide_them('one', 44)
assert_true(true, 'passing assert')
"""
# this one has to fool the parser with "get_first" otherwise the parser
# will catch it and it errors on making the script.
var _src_script_error_in_test = """
func get_first():
return "one"
func test_with_script_error():
var a = get_first() / 44
assert_true(true, 'passing assert')
"""
func test_push_error_causes_failure():
var s = autofree(DynamicGutTest.new())
s.add_source(_src_push_error)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1, 'one failing')
assert_eq(t.passing, 1, 'one passing')
func test_gut_error_causes_failure():
var s = autofree(DynamicGutTest.new())
s.add_source(_src_gut_error)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1, 'one failing')
assert_eq(t.passing, 1, 'one passing')
func test_script_error_causes_failure():
var s = autofree(DynamicGutTest.new())
s.add_source(_src_script_error_in_called_method)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1, 'one failing')
assert_eq(t.passing, 1, 'one passing')
func test_script_error_in_test_causes_failure():
var s = autofree(DynamicGutTest.new())
s.add_source(_src_script_error_in_test)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1, 'one failing')
assert_eq(t.passing, 0, 'no passing because of early exit from error')
class TestErrorAsserts:
extends GutInternalTester
func should_skip_script():
return skip_if_debugger_active()
var _gut = null
func before_all():
verbose = false
gut.error_tracker.disabled = true
DynamicGutTest.should_print_source = verbose
func before_each():
_gut = add_child_autofree(new_gut(verbose))
GutErrorTracker.register_logger(_gut.error_tracker)
func after_each():
GutErrorTracker.deregister_logger(_gut.error_tracker)
func after_all():
gut.error_tracker.disabled = false
var _src_divide_them = """
func divide_them(a, b):
return a / b
"""
# ---------------------
# assert_push_error_count
# ---------------------
func test_asserting_one_push_error_prevents_failure():
var test_func = func(me):
push_error('error 1')
me.assert_push_error_count(1)
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_asserting_two_push_error_prevents_failure():
var test_func = func(me):
push_error('error 1')
push_error('error 2')
me.assert_push_error_count(2)
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_asserting_non_matching_push_error_causes_failure():
var test_func = func(me):
push_error('error 1')
push_error('error 2')
push_error('error 3')
me.assert_push_error_count(1)
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
# 2 failures, one for assert and one for the unexpected errors that
# were not handled by the assert
assert_eq(t.failing, 2)
# ---------------------
# assert_push_error
# ---------------------
func test_push_error_with_matching_text_prevents_failure():
var test_func = func(me):
push_error('_special_ text')
me.assert_push_error('_special_')
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_push_error_with_non_matching_text_fails():
var test_func = func(me):
push_error('_special_ text')
me.assert_push_error('nope')
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 2)
func test_push_error_with_matching_text_only_consumes_one():
var test_func = func(me):
push_error('_special_ one')
push_error('_special_ two')
me.assert_push_error('_special_')
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
assert_eq(t.failing, 1)
func test_push_error_can_assert_multiple_different_texts():
var test_func = func(me):
push_error('_special_ one')
push_error('_special_ two')
me.assert_push_error('one')
me.assert_push_error('two')
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 2)
assert_eq(t.failing, 0)
func test_asserting_multiple_push_error_with_same_text():
var test_func = func(me):
push_error('distinct_error')
push_error('distinct_error')
me.assert_push_error("distinct_error")
me.assert_push_error("distinct_error")
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 2)
assert_eq(t.failing, 0)
# ---------------------
# assert_engine_error_count
# ---------------------
func test_asserting_engine_error_prevents_failure():
var test_func = func(me):
me.divide_them(1, 'b')
me.assert_engine_error_count(1)
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_asserting_multiple_engine_error_prevents_failure():
var test_func = func(me):
me.divide_them(1, 'b')
me.divide_them(1, 'b')
me.divide_them(1, 'b')
me.assert_engine_error_count(3)
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_asserting_non_matching_count_causes_two_failures():
var test_func = func(me):
me.divide_them(1, 'b')
me.divide_them(1, 'b')
me.divide_them(1, 'b')
me.assert_engine_error_count(2)
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
# 2 failures, one for assert and one for the unexpected errors that
# were not handled by the assert
assert_eq(t.failing, 2)
# ---------------------
# Engine error text
# ---------------------
func test_engine_with_matching_text_prevents_failure():
var test_func = func(me):
me.divide_them(1, 'b')
me.assert_engine_error('Invalid operands')
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_engine_with_non_matching_text_fails():
var test_func = func(me):
me.divide_them(1, 'b')
me.assert_engine_error('nope')
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 2)
func test_engine_with_matching_text_only_consumes_one():
var test_func = func(me):
me.divide_them(1, 'b')
me.divide_them(1, 'b')
me.assert_engine_error('Invalid operands')
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
assert_eq(t.failing, 1)
func test_engine_can_assert_multiple_different_texts():
var test_func = func(me):
me.divide_them(1, 'b')
me.divide_them(1, 0)
me.assert_engine_error('Invalid operands')
me.assert_engine_error('Division by zero')
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 2)
assert_eq(t.failing, 0)
# ---------------------
# assert_push_warning_count
# ---------------------
func test_asserting_one_push_warning_when_none_causes_failure():
var test_func = func(me):
me.assert_push_warning_count(1)
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1)
func test_asserting_matching_push_warning_count_passes():
var test_func = func(me):
push_warning('warn 1')
me.assert_push_warning_count(1)
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_asserting_two_push_warnings_passes():
var test_func = func(me):
push_warning('warn 1')
push_warning('warn 2')
me.assert_push_warning_count(2)
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
# ---------------------
# assert_push_warning
# ---------------------
func test_asserting_push_warning_when_no_warnings_causes_failure():
var test_func = func(me):
me.assert_push_warning("nothing")
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1)
func test_asserting_push_warning_with_matching_text_passes():
var test_func = func(me):
push_warning("warn 1")
me.assert_push_warning("warn")
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1)
func test_asserting_non_matching_push_warning_causes_failure():
var test_func = func(me):
push_warning('warn 1')
push_warning('warn 2')
push_warning('warn 3')
me.assert_push_warning('hello')
var s = autofree(DynamicGutTest.new())
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1)
# ---------------------
# misc
# ---------------------
func test_can_assert_multiple_error_types():
var test_func = func(me):
me.divide_them(1, 'b')
me.divide_them(1, 'b')
push_error('push error 1')
me.assert_engine_error_count(2)
me.assert_push_error_count(1)
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 2)
# ---------------------
# get_errors
# ---------------------
func test_can_get_all_errors_that_occur():
var test_func = func(me):
me.divide_them(1, 'b')
me.divide_them(1, 'b')
push_error('pushe error 1')
var errs = me.get_errors()
me.assert_eq(errs.size(), 3)
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1, 'pass count')
# Errors are not consumed so they still cause errors.
assert_eq(t.failing, 1, 'fail count')
func test_can_mark_all_errors_handled_manually():
var test_func = func(me):
me.divide_them(1, 'b')
me.divide_them(1, 'b')
push_error('push error 1')
var errs = me.get_errors()
me.assert_eq(errs.size(), 3)
for e in errs:
e.handled = true
var s = autofree(DynamicGutTest.new())
s.add_source(_src_divide_them)
s.add_lambda_test(test_func, 'test_something')
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 1, 'pass count')
# Errors are not consumed so they still cause errors.
assert_eq(t.failing, 0, 'fail count')
func test_parameterized_tests_do_not_compound_error_counts():
var src_test_with_params = """
var params = [1, 2, 3, 4, 5]
func test_parameterized_and_errors(p=use_parameters(params)):
push_error(str("Error ", p))
assert_push_error_count(1)
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src_test_with_params)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 5, '5 params, 1 assert each: pass count')
assert_eq(t.failing, 0, 'fail count')

View File

@@ -0,0 +1 @@
uid://3bg1hnu6os70

View File

@@ -0,0 +1,25 @@
extends GutInternalTester
func test_can_get_spy():
var g = autofree(new_gut(verbose))
assert_ne(g.get_spy(), null)
func test_spy_for_doubler_is_guts_spy():
var g = autofree(new_gut(verbose))
assert_eq(g.get_doubler().get_spy(), g.get_spy())
# ---------------------------------
# these two tests use the gut instance that is passed to THIS test. This isn't
# PURE testing but it appears to cover the bases ok.
class TestGutClearsSpyBetweenTests:
extends 'res://addons/gut/test.gd'
func test_spy_cleared_between_tests_setup():
gut.get_spy().add_call('thing', 'method')
assert_true(gut.get_spy().was_called('thing', 'method'))
func test_spy_cleared_between_tests():
assert_false(gut.get_spy().was_called('thing', 'method'))
# ---------------------------------

View File

@@ -0,0 +1 @@
uid://brp8tj0vlp8vb

View File

@@ -0,0 +1,51 @@
extends GutInternalTester
var StubParams = load('res://addons/gut/stub_params.gd')
func test_can_get_stubber():
var g = autofree(new_gut(verbose))
assert_ne(g.get_stubber(), null)
# ---------------------------------
# these two tests use the gut instance that is passed to THIS test. This isn't
# PURE testing but it appears to cover the bases ok.
# ------
func test_stubber_cleared_between_tests_setup():
var sp = StubParams.new('thing', 'method').to_return(5)
gut.get_stubber().add_stub(sp)
pass_test('this sets up for next test')
func test_stubber_cleared_between_tests():
assert_eq(gut.get_stubber().get_return('thing', 'method'), null)
# ---------------------------------
func test_can_get_doubler():
var g = autofree(new_gut(verbose))
assert_ne(g.get_doubler(), null)
func test_doublers_stubber_is_guts_stubber():
var g = autofree(new_gut(verbose))
assert_eq(g.get_doubler().get_stubber(), g.get_stubber())
# Since the stubber and doubler are "global" to gut, this is the best place
# to test this so that the _double_count in the doubler isn't reset which
# causes some super confusing side effects. This test is here because the
# opposite used to be true when things were indexed in the stubber by their
# path. This is no longer possible to do, but it doesn't seem like a big
# loss, you can still stub instances just fine. Not sure of a scenario where
# you would want to stub all instances of a scene one way and all instances
# of a script another way. And if there is a scenrio where this is needed, you
# can just stub all the instances.
func test_scene_and_script_are_the_same_when_stubbing_resource():
var script_path = DOUBLE_ME_SCENE_PATH.replace('.tscn', '.gd')
var scene = double(DoubleMeScene).instantiate()
var script = double(load(script_path)).new()
# order here matters. The 2nd will overwrite the first.
stub(DOUBLE_ME_SCENE_PATH, 'return_hello').to_return('scene')
stub(script_path, 'return_hello').to_return('script')
assert_eq(scene.return_hello(), 'script')
assert_eq(script.return_hello(), 'script')

View File

@@ -0,0 +1 @@
uid://bedsk56wsq44a

View File

@@ -0,0 +1,84 @@
extends GutInternalTester
var _test_gut = null
const EXPORT_FILE = 'res://test/exported_tests.cfg'
func before_each():
_test_gut = new_gut()
_test_gut.log_level = 4
# _test_gut._should_print_versions = false
autofree(_test_gut)
func after_each():
gut.file_delete(EXPORT_FILE)
_test_gut = null
func test_export_test_exports_tests():
_test_gut.add_directory('res://test/resources/parsing_and_loading_samples')
_test_gut.export_tests(EXPORT_FILE)
assert_file_not_empty(EXPORT_FILE)
func test_export_uses_export_path_if_no_path_sent():
_test_gut.add_directory('res://test/resources/parsing_and_loading_samples')
_test_gut.export_path = EXPORT_FILE
_test_gut.export_tests()
assert_file_not_empty(EXPORT_FILE)
func test_if_export_path_not_set_and_no_path_passed_error_is_generated():
_test_gut.add_directory('res://test/resources/parsing_and_loading_samples')
_test_gut.export_tests()
assert_tracked_gut_error(_test_gut)
func test_importing_tests_populates_test_collector():
_test_gut.add_directory('res://test/resources/parsing_and_loading_samples')
_test_gut.export_tests(EXPORT_FILE)
var _import_gut = add_child_autofree(new_gut())
_import_gut.import_tests(EXPORT_FILE)
assert_eq(
_import_gut.get_test_collector().scripts.size(),
_test_gut.get_test_collector().scripts.size())
func test_import_tests_uses_export_path_by_default():
_test_gut.add_directory('res://test/resources/parsing_and_loading_samples')
_test_gut.export_tests(EXPORT_FILE)
var _import_gut = add_child_autofree(new_gut())
_import_gut.export_path = EXPORT_FILE
_import_gut.import_tests()
assert_eq(
_import_gut.get_test_collector().scripts.size(),
_test_gut.get_test_collector().scripts.size())
func test_import_errors_if_file_does_not_exist():
_test_gut.import_tests('res://file_does_not_exist.txt')
assert_tracked_gut_error(_test_gut)
func test_gut_runs_the_imported_tests():
pending('this is failing and I think it is related to import/export not working yet.')
return
_test_gut.add_directory('res://test/resources/parsing_and_loading_samples')
_test_gut.export_tests(EXPORT_FILE)
var _import_gut = new_gut()
add_child(_import_gut)
_import_gut.export_path = EXPORT_FILE
_import_gut.import_tests()
_import_gut.test_scripts()
var import_totals = _import_gut.get_summary().get_totals()
# The magic numbers in these asserts were picked from running the same set
# of scripts through the test_summary.gd script and checking the output.
assert_eq(import_totals.scripts, 13, 'total scripts')
assert_eq(import_totals.tests, 37, 'total tests')
# picked some arbitrary number since these assert counts could change
# over time. Last run was 16 passing. This is probably a sign that this
# shouldn't be reusing parsing_and_loading_samples but the world isn't
# perfect alright? I'm trying here, but lay off a bit why dontcha.
assert_gt(import_totals.passing, 10, 'min total passing')
_import_gut.free()

View File

@@ -0,0 +1 @@
uid://chc2xdt1g5qs

View File

@@ -0,0 +1,59 @@
extends GutTest
class CoupledScriptTest:
extends GutInternalTester
var _gut = null
func before_each():
_gut = new_gut(verbose)
_gut._should_print_versions = false
_gut._should_print_summary = false
add_child_autofree(_gut)
func _same_name():
return gut.get_current_test_object().name
func _run_tests(script_path, inner_class, test_name):
_gut.add_script(script_path)
if(inner_class != null):
_gut.inner_class_name = inner_class
if(test_name != null):
_gut.unit_test_name = test_name
_gut.wait_log_delay = wait_log_delay
_gut.test_scripts()
func _assert_pass_fail_count(passing, failing):
assert_eq(_gut.get_pass_count(), passing, 'Pass count does not match')
assert_eq(_gut.get_fail_count(), failing, 'Failing count does not match')
class TestYieldInBeforeAfterMethods:
extends CoupledScriptTest
const SCRIPT_PATH = 'res://test/resources/yield_in_before_after_methods.gd'
func test_gut_waits_for_yield_in_before_all():
_run_tests(SCRIPT_PATH, 'TestYieldInBeforeAll', null)
await wait_for_signal(_gut.end_run, 10)
_assert_pass_fail_count(1, 0)
func test_gut_waits_for_yield_in_after_all():
var start_time = Time.get_ticks_msec()
_run_tests(SCRIPT_PATH, 'TestYieldInAfterAll', null)
await wait_for_signal(_gut.end_run, 10)
assert_gt(Time.get_ticks_msec() - start_time, 1000)
func test_gut_waits_for_yield_in_after_each():
_run_tests(SCRIPT_PATH, 'TestYieldInAfterEach', null)
await wait_for_signal(_gut.end_run, 10)
_assert_pass_fail_count(1, 1)
func test_gut_waits_for_yield_in_before_each():
_run_tests(SCRIPT_PATH, 'TestYieldInBeforeEach', null)
await wait_for_signal(_gut.end_run, 10)
_assert_pass_fail_count(1, 0)

View File

@@ -0,0 +1 @@
uid://dv1bo3sdwop65

View File

@@ -0,0 +1,132 @@
extends GutInternalTester
var _src_passing_test = """
func test_is_passing():
assert_true(true)
"""
var _src_should_skip_script_method_ret_true = """
func should_skip_script():
return true
"""
var _src_should_skip_script_method_ret_false = """
func should_skip_script():
return false
"""
var _src_should_skip_script_method_ret_string = """
func should_skip_script():
return 'skip me'
"""
var _scr_awaiting_should_skip_script = """
func should_skip_script():
print("Awaiting before skipping.")
await wait_seconds(1)
print("Now skip.")
return true
"""
var _gut = null
func before_all():
verbose = false
DynamicGutTest.should_print_source = verbose
func before_each():
_gut = add_child_autofree(new_gut(verbose))
# --------------
# skip var
# --------------
func test_using_skip_script_variable_is_deprecated():
var s = DynamicGutTest.new()
s.add_source("var skip_script = 'skip me thanks'")
s.add_source(_src_passing_test)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.deprecated, 1, 'Should be one deprecation.')
func test_when_skip_script_var_is_string_script_is_skipped():
var s = DynamicGutTest.new()
s.add_source("var skip_script = 'skip me'")
s.add_source(_src_passing_test)
var smry = await s.run_tests_in_gut_await(_gut)
assert_eq(smry.tests, 0, 'no tests should be ran')
assert_eq(smry.risky, 1, 'Should be marked as risky due to skip')
func test_when_skip_script_var_is_null_the_script_is_ran():
var s = DynamicGutTest.new()
s.add_source("var skip_script = null")
s.add_source(_src_passing_test)
var smry = await s.run_tests_in_gut_await(_gut)
assert_eq(smry.tests, 1, 'the one test should be ran')
assert_eq(smry.risky, 0, 'not marked risky just for having var')
func test_when_skip_scrpt_var_is_true_the_script_is_skipped():
var s = DynamicGutTest.new()
s.add_source("var skip_script = true")
s.add_source(_src_passing_test)
var smry = await s.run_tests_in_gut_await(_gut)
assert_eq(smry.tests, 0, 'no tests should be ran')
assert_eq(smry.risky, 1, 'Should be marked as risky due to skip')
func test_awaiting_before_should_skip_script():
var s = DynamicGutTest.new()
s.add_source(_scr_awaiting_should_skip_script)
await s.run_tests_in_gut_await(_gut)
await wait_for_signal(_gut.end_run, 3, "Should take exactly 1 second")
var summery = GutUtils.Summary.new()
var totals = summery.get_totals(_gut)
assert_eq(totals.tests, 0, 'no tests should be ran')
assert_eq(totals.risky, 1, 'Should be marked as risky due to skip')
# --------------
# skip method
# --------------
func test_should_skip_script_method_returns_false_by_default():
var test = autofree(GutTest.new())
assert_false(test.should_skip_script())
func test_when_should_skip_script_returns_false_script_is_run():
var s = DynamicGutTest.new()
s.add_source(_src_should_skip_script_method_ret_false)
s.add_source(_src_passing_test)
var smry = await s.run_tests_in_gut_await(_gut)
assert_eq(smry.tests, 1, 'Tests should run')
assert_eq(smry.risky, 0, 'Should not be risky')
func test_when_should_skip_script_returns_true_script_is_skipped():
var s = DynamicGutTest.new()
s.add_source(_src_should_skip_script_method_ret_true)
s.add_source(_src_passing_test)
var smry = await s.run_tests_in_gut_await(_gut)
assert_eq(smry.tests, 0, 'no tests should be ran')
assert_eq(smry.risky, 1, 'Should be marked as risky due to skip')
func test_when_should_skip_script_returns_string_script_is_skipped():
var s = DynamicGutTest.new()
s.add_source(_src_should_skip_script_method_ret_string)
s.add_source(_src_passing_test)
var smry = await s.run_tests_in_gut_await(_gut)
assert_eq(smry.tests, 0, 'no tests should be ran')
assert_eq(smry.risky, 1, 'Should be marked as risky due to skip')
func test_using_should_skip_script_method_is_not_deprecated():
var s = DynamicGutTest.new()
s.add_source(_src_should_skip_script_method_ret_true)
s.add_source(_src_passing_test)
var smry = await s.run_tests_in_gut_await(_gut)
assert_eq(smry.deprecated, 0, 'nothing is deprecated')

View File

@@ -0,0 +1 @@
uid://beskegy2ok1wg

View File

@@ -0,0 +1,16 @@
extends GutTest
var scene_path = "res://test/resources/simple_scene.tscn"
var Scene = load(scene_path)
func test_with_change_scene_to_file():
get_tree().change_scene_to_file(scene_path)
await get_tree().scene_changed
autofree(get_tree().current_scene)
pass_test('passing')
func test_with_change_scene_to_packed():
get_tree().change_scene_to_packed(Scene)
await get_tree().scene_changed
autofree(get_tree().current_scene)
pass_test('passing')

View File

@@ -0,0 +1 @@
uid://igdfkcutuncs

View File

@@ -0,0 +1,117 @@
extends GutInternalTester
var JunitExporter = GutUtils.JunitXmlExport
var _test_gut = null
const RESULT_XML_VALID_TAGS := {
"testsuite": [ "name", "tests", "failures", "skipped", "time" ],
"testcase": [ "name", "assertions", "status", "classname", "time" ],
"testsuites": [ "name", "failures", "tests" ],
"failure": [ "message" ],
"skipped": [ "message" ]
}
# Returns a new gut object, all setup for testing.
func get_a_gut():
var g = new_gut(verbose)
return g
func run_scripts(g, one_or_more):
var scripts = one_or_more
if(typeof(scripts) != TYPE_ARRAY):
scripts = [scripts]
for s in scripts:
g.add_script(export_script(s))
g.test_scripts()
await g.end_run
# Very simple xml validator. Matches closing tags to opening tags as they
# are encountered and any validation provided by XMLParser (which is very
# little). Does not catch malformed attributes among other things probably.
# Additionally checks for valid tags as defined by RESULT_XML_VALID_TAGS
func assert_is_valid_xml(xml : String)->void:
var tags = []
var pba = xml.to_utf8_buffer()
var parser = XMLParser.new()
var result = parser.open_buffer(pba)
while(result == OK):
if(parser.get_node_type() == parser.NODE_ELEMENT):
var tag_name := parser.get_node_name()
tags.push_back(tag_name)
if (tag_name in RESULT_XML_VALID_TAGS):
# check for required attributes
var required_attributes : Array = RESULT_XML_VALID_TAGS[tag_name].duplicate()
var missing_attributes := required_attributes.filter(func(attribute): return !parser.has_attribute(attribute))
assert_eq(missing_attributes, [], str(tag_name, ": Required attribute(s) missing ", missing_attributes))
# check for unexpected attributes
var unexpected_attributes : Array[String] = []
for attribute_index : int in parser.get_attribute_count():
var attribute_name := parser.get_attribute_name(attribute_index)
if not attribute_name in RESULT_XML_VALID_TAGS[tag_name]:
unexpected_attributes.push_back(attribute_name)
assert_eq(unexpected_attributes, [], str(tag_name, " Unexpected attribute(s) ", unexpected_attributes))
else:
fail_test("%s is not one of the expected tags: %s" % [tag_name, RESULT_XML_VALID_TAGS.keys()])
elif(parser.get_node_type() == parser.NODE_ELEMENT_END):
var last_tag = tags.pop_back()
if(last_tag != parser.get_node_name()):
var msg = str("End tag does not match. Expected: ", last_tag, ', got: ', parser.get_node_name())
push_error(msg)
result = -1
if(result != -1):
result = parser.read()
assert_eq(result, ERR_FILE_EOF, 'Parsing xml should reach EOF')
func export_script(n):
return str('res://test/resources/exporter_test_files/', n)
func before_each():
_test_gut = get_a_gut()
add_child_autoqfree(_test_gut)
func test_can_make_one():
assert_not_null(JunitExporter.new())
func test_no_tests_returns_valid_xml():
await run_scripts(_test_gut, [])
var re = JunitExporter.new()
var result = re.get_results_xml(_test_gut)
assert_is_valid_xml(result)
func test_spot_check():
await run_scripts(_test_gut, ['test_simple_2.gd', 'test_simple.gd', 'test_with_inner_classes.gd', 'test_special_chars_in_test_output.gd'])
var re = JunitExporter.new()
var result = re.get_results_xml(_test_gut)
assert_is_valid_xml(result)
func test_res_removed_from_classname_path():
await run_scripts(_test_gut, 'test_simple_2.gd')
var re = JunitExporter.new()
var result = re.get_results_xml(_test_gut)
assert_false(result.contains("classname=\"res://test/resources/exporter_test_files/test_simple_2.gd\""))
assert_string_contains(result, "classname=\"test/resources/exporter_test_files/test_simple_2.gd\"")
func test_write_file_creates_file():
await run_scripts(_test_gut, 'test_simple_2.gd')
var fname = "user://test_junit_exporter.xml"
var re = JunitExporter.new()
re.write_file(_test_gut, fname)
assert_file_not_empty(fname)
gut.file_delete(fname)
func test_xml_is_valid_when_test_skip_message_is_null():
await run_scripts(_test_gut, ['test_special_chars_in_test_output.gd'])
var re = JunitExporter.new()
var result = re.get_results_xml(_test_gut)
assert_is_valid_xml(result)

View File

@@ -0,0 +1 @@
uid://dul1tj2lxkeev

View File

@@ -0,0 +1,191 @@
extends GutTest
class SuperButton:
extends Button
func p(s1='', s2='', s3='', s4='', s5='', s6=''):
print(s1, s2, s3, s4, s5, s6)
func pevent(txt, event):
return
print(txt, ': ', event)
# if(event is InputEventMouse):
# print(txt, ': ', event.position, event.global_position)
# else:
# print(txt, ': ', event)
func _gui_input(event):
pevent('gui: ', event)
func _input(event):
pevent('input: ', event)
func _unhandled_input(event):
pevent('unhandled: ', event)
class DraggableButton:
extends SuperButton
var _mouse_down = false
func _gui_input(event):
super._gui_input(event)
if(event is InputEventMouseButton):
_mouse_down = event.pressed
elif(event is InputEventMouseMotion and _mouse_down):
position += event.relative
func should_skip_script():
return 'takes too long and these shouldnt even be here'
func _print_emitted_signals(thing):
_signal_watcher.print_signal_summary(thing)
return
func test_draw_mouse():
var sender = InputSender.new(Input)
sender.mouse_warp = false
sender.draw_mouse = true
var pos = Vector2(200, 200)
sender\
.mouse_left_button_down(pos)\
.wait(1)\
.mouse_left_button_up()\
.mouse_right_button_down()\
.wait(1)\
.mouse_right_button_up()\
.mouse_relative_motion(Vector2(10, 10)).wait(.5)\
.mouse_relative_motion(Vector2(10, 10)).wait(.5)\
.mouse_left_button_down().hold_for(.5)\
.mouse_relative_motion(Vector2(10, 10)).wait(.5)\
.mouse_relative_motion(Vector2(10, 10)).wait(.5)\
.mouse_right_button_down().hold_for(.5)\
.mouse_relative_motion(Vector2(10, 10)).wait(.5)\
.mouse_left_button_down()\
.mouse_right_button_down()\
.wait(2)
await sender.idle
func test_drag_something():
var btn = DraggableButton.new()
watch_signals(btn)
btn.size = Vector2(100, 100)
btn.position = Vector2(50, 50)
add_child_autofree(btn)
# works with Input and btn, btn does not fire signals, Input seems to be
# having some trouble firigin the button up event.
var sender = InputSender.new(Input)
sender.set_auto_flush_input(true)
sender.mouse_warp = false
sender.draw_mouse = true
sender.mouse_left_button_down(btn.position + Vector2(10, 10)).wait(.1)
for i in range(10):
await sender.mouse_relative_motion(Vector2(10, 10)).wait(.1).idle
print('-- ', btn.position, ' --')
assert_true(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT), 'left button is down')
await sender\
.mouse_left_button_up()\
.wait('1f')\
.mouse_relative_motion(Vector2(1, 1))\
.wait(.2).idle
assert_false(Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT), 'left button is up')
var after_first_drag_pos = btn.position
# drag again after mouse up which shouldn't move
for i in range(10):
await sender.mouse_relative_motion(Vector2(10, 10)).wait(.1).idle
print('-- ', btn.position, ' --')
_print_emitted_signals(btn)
assert_signal_emitted(btn, 'button_down')
assert_signal_emitted(btn, 'button_up')
assert_ne(btn.position, Vector2(50, 50), 'has moved')
assert_false(btn._mouse_down, 'button mouse down')
assert_eq(btn.position, after_first_drag_pos, 'does not move after releasing button')
# 50 ->| |<- 150
func test_clicking_things_with_input_as_receiver():
var btn = SuperButton.new()
watch_signals(btn)
btn.size = Vector2(100, 100)
btn.position = Vector2(50, 50)
add_child_autofree(btn)
var sender = InputSender.new(Input)
sender.mouse_warp = true
var start_pos = Vector2i(25, 75)
for i in 15:
var new_pos = start_pos + Vector2i(i * 10, 0)
await sender.wait(.1)\
.mouse_left_button_down(new_pos)\
.hold_for(.1)\
.wait(.1).idle
_print_emitted_signals(btn)
assert_signal_emitted(btn, 'pressed')
assert_signal_emitted(btn, 'button_down')
assert_signal_emitted(btn, 'button_up')
assert_signal_emitted(btn, 'gui_input')
func test_clicking_two_buttons_triggers_focus_events():
var btn = SuperButton.new()
watch_signals(btn)
btn.size = Vector2(100, 100)
btn.position = Vector2(50, 50)
add_child_autofree(btn)
var btn2 = SuperButton.new()
watch_signals(btn2)
btn2.size = Vector2(100, 100)
btn2.position = Vector2(160, 50)
add_child_autofree(btn2)
var sender = InputSender.new(Input)
sender.mouse_warp = true
var start_pos = Vector2(100, 75)
for i in 10:
var new_pos = start_pos + Vector2(i * 10, 0)
await sender.mouse_left_click_at(new_pos).idle
_print_emitted_signals(btn)
_print_emitted_signals(btn2)
func test_clicking_things_with_button_as_receiver():
var btn = SuperButton.new()
watch_signals(btn)
btn.size = Vector2(100, 100)
btn.position = Vector2(50, 50)
add_child_autofree(btn)
var sender = InputSender.new(btn)
sender.mouse_warp = true
var start_pos = Vector2i(25, 75)
for i in 15:
var new_pos = start_pos + Vector2i(i * 10, 0)
await sender.wait(.1)\
.mouse_left_button_down(new_pos)\
.hold_for(.1)\
.wait(.1).idle
_print_emitted_signals(btn)
assert_signal_not_emitted(btn, 'pressed')
assert_signal_not_emitted(btn, 'button_down')
assert_signal_not_emitted(btn, 'button_up')
assert_signal_not_emitted(btn, 'gui_input')

View File

@@ -0,0 +1 @@
uid://b2ppcuklb50i3

View File

@@ -0,0 +1,12 @@
#A sample script for illustrating multiple scripts and what it looks like
#when all tests pass.
extends "res://addons/gut/test.gd"
func test_works():
assert_true(true, 'This is true')
func test_two():
assert_eq("two", "two", "This is also true")
func test_3():
assert_ne("one", "two", "This is yet again true")

View File

@@ -0,0 +1 @@
uid://cow6ssjc4kran

View File

@@ -0,0 +1,12 @@
extends GutInternalTester
func before_each():
stub(DoubleMe, 'get_value').to_return(999)
func test_that_it_is_stubbed():
var inst = double(DoubleMe).new()
assert_eq(inst.get_value(), 999)

View File

@@ -0,0 +1 @@
uid://cagug263hk5yf

View File

@@ -0,0 +1,71 @@
extends GutTest
var Stubber = load('res://addons/gut/stubber.gd')
var Doubler = load('res://addons/gut/doubler.gd')
var StubParams = load('res://addons/gut/stub_params.gd')
class HasAccessors:
var string_normal_accessors = 'default' :
get: return string_normal_accessors
set(val): string_normal_accessors = val
var string_accessor_method = 'default' :
get = _get_string_accessor_method,
set = _set_string_accessor_method
func _get_string_accessor_method():
return string_accessor_method
func _set_string_accessor_method(val):
string_accessor_method = val
func this_is_a_normal_method():
print('you called this normal method')
var doubler = null
var stubber = null
func before_each():
doubler = Doubler.new(GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE)
# doubler.print_source = true
doubler.inner_class_registry.register(self.get_script())
stubber = GutUtils.Stubber.new()
doubler.set_stubber(stubber)
func test_can_double_scripts_with_accessors():
var DoubleHasAccessors = doubler.double(HasAccessors)
var inst = DoubleHasAccessors.new()
assert_not_null(inst)
func test_normal_get_accessor_not_stubbed():
var DoubleHasAccessors = doubler.double(HasAccessors)
var dbl_ha = DoubleHasAccessors.new()
assert_eq(dbl_ha.string_normal_accessors, 'default')
func test_normal_set_accessor_not_stubbed():
var DoubleHasAccessors = doubler.double(HasAccessors)
var dbl_ha = DoubleHasAccessors.new()
dbl_ha.string_normal_accessors = 'foo'
assert_eq(dbl_ha.string_normal_accessors, 'foo')
func test_get_accessor_method_is_stubbed_to_do_nothing():
var dbl_ha = doubler.double(HasAccessors).new()
assert_null(dbl_ha.string_accessor_method)
func test_set_accessor_method_is_stubbed_to_do_nothing():
var dbl_ha = doubler.double(HasAccessors).new()
var sp1 = StubParams.new(dbl_ha, '_get_string_accessor_method').to_call_super()
stubber.add_stub(sp1)
dbl_ha.string_accessor_method = 'bar'
assert_eq(dbl_ha.string_accessor_method, 'default')
func test_partial_double_works():
var dbl_ha = doubler.partial_double(HasAccessors).new()
dbl_ha.string_normal_accessors = 'foo'
dbl_ha.string_accessor_method = 'bar'
assert_eq(dbl_ha.string_normal_accessors, 'foo')
assert_eq(dbl_ha.string_accessor_method, 'bar')

View File

@@ -0,0 +1 @@
uid://bhdwm7ifmk0at

View File

@@ -0,0 +1,289 @@
extends GutInternalTester
var _gut = null
var _base_src = """
func make_node(node_name):
var n = Node.new()
n.name = node_name
return n
"""
func before_all():
verbose = false
func before_each():
_gut = add_child_autofree(new_gut(verbose))
var l = _gut.get_logger()
l.set_type_enabled(l.types.orphan, verbose)
if(!verbose):
_gut.log_level = 0
func _free_orphans():
var ids = Node.get_orphan_node_ids()
for id in ids:
if(is_instance_id_valid(id)):
var n = instance_from_id(id)
n.free()
func _run_test_script_source(src, g):
var s = autofree(DynamicGutTest.new())
s.add_source(_base_src)
s.add_source(src)
return await s.run_tests_in_gut_await(g)
func assert_total_orphans_recorded(g, count):
var oc = g.get_orphan_counter()
var ids = oc.get_orphan_ids()
assert_eq(ids.size(), count, "Recorded orphan count")
func assert_total_fail_pass(totals, fail_count, pass_count):
assert_eq(totals.failing, fail_count, 'Expected fail count')
assert_eq(totals.passing, pass_count, 'Expected pass count')
# --------------------------
# Test related counts
# --------------------------
func test_orphans_made_in_test_cause_failure():
var src = """
func test_the_test():
var n = make_node('test_the_test')
assert_no_new_orphans()
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 0)
_free_orphans()
func test_script_level_orphans_do_not_appear_as_test_orphans():
var src = """
var n = make_node('script_level')
func test_the_test():
assert_no_new_orphans()
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 0, 1)
assert_total_orphans_recorded(_gut, 1)
_free_orphans()
func test_orphans_are_not_counted_twice_in_a_test():
var src = """
func test_the_test():
make_node('made_an_orphan')
assert_no_new_orphans()
assert_no_new_orphans()
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
assert_total_orphans_recorded(_gut, 1)
_free_orphans()
func test_orphans_no_orphans_then_orphans_again_in_a_test():
var src = """
func test_the_test():
make_node('made_an_orphan')
assert_no_new_orphans()
assert_no_new_orphans()
make_node('made_an_orphan')
assert_no_new_orphans()
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 2, 1)
assert_total_orphans_recorded(_gut, 2)
_free_orphans()
# --------------------------
# after_all
# --------------------------
func test_checking_for_orphans_in_after_all_is_ok():
var src = """
func after_all():
assert_no_new_orphans()
func test_the_test():
pass_test('this is passing')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 0, 2)
func test_orphans_made_after_test_found_in_after_all():
var src = """
func after_all():
await wait_frames(10)
assert_no_new_orphans()
func test_the_test():
make_node.call_deferred('test_the_test')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 0)
_free_orphans()
func test_orphans_made_in_after_all_are_found_in_after_all():
var src = """
func after_all():
make_node('made_in_after_all')
assert_no_new_orphans()
func test_the_test():
pass_test('this is passing')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
_free_orphans()
func test_non_asserted_orphans_are_found_in_after_all():
var src = """
func after_all():
assert_no_new_orphans()
func test_the_test():
make_node('test_the_test')
pass_test('this is passing')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
assert_total_orphans_recorded(_gut, 1)
_free_orphans()
func test_asserted_orphans_are_found_in_after_all():
var src = """
func after_all():
assert_no_new_orphans()
func test_the_test():
make_node('test_the_test')
assert_no_new_orphans()
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 2, 0)
assert_total_orphans_recorded(_gut, 1)
_free_orphans()
func test_orphans_made_in_after_each_are_found_in_after_all():
var src = """
func after_all():
assert_no_new_orphans()
func after_each():
make_node('test_the_test')
func test_the_test():
make_node('test_the_test')
pass_test('this is passing')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
assert_total_orphans_recorded(_gut, 2)
_free_orphans()
func test_script_level_orphans_found_in_after_all():
var src = """
var n = make_node('script_level')
func after_all():
assert_no_new_orphans('after_all')
gut.get_orphan_counter().log_all()
func test_the_test():
pass_test('this is passing')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
assert_total_orphans_recorded(_gut, 1)
_free_orphans()
func test_orphans_no_orphans_then_orphans_again_in_a_test_then_after_all():
var src = """
func after_all():
assert_no_new_orphans()
func test_the_test():
make_node('made_an_orphan')
assert_no_new_orphans()
assert_no_new_orphans()
make_node('made_an_orphan')
assert_no_new_orphans()
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 3, 1)
assert_total_orphans_recorded(_gut, 2)
_free_orphans()
func test_freed_orphans_do_not_cause_failure_in_after_all():
var src = """
var n = null
func after_all():
assert_no_new_orphans()
func after_each():
n.free()
func test_the_test():
n = make_node('made_an_orphan')
assert_no_new_orphans()
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
assert_total_orphans_recorded(_gut, 0)
_free_orphans()
# --------------------------
# after_each
# --------------------------
func test_non_asserted_orphans_are_found_in_after_each():
var src = """
func after_each():
assert_no_new_orphans()
func test_the_test():
make_node('test_the_test')
pass_test('this is passing')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
assert_total_orphans_recorded(_gut, 1)
_free_orphans()
# --------------------------
# before_all
# --------------------------
func test_script_level_orphans_found_in_before_all():
var src = """
var n = make_node('script_level')
func before_all():
assert_no_new_orphans()
func test_the_test():
pass_test('this is passing')
"""
var t = await _run_test_script_source(src, _gut)
assert_total_fail_pass(t, 1, 1)
assert_total_orphans_recorded(_gut, 1)
_free_orphans()

View File

@@ -0,0 +1 @@
uid://d3f2kj612lc7p

View File

@@ -0,0 +1,3 @@
extends GutInternalTester
# Spot check passing conditions for tests.

View File

@@ -0,0 +1 @@
uid://ddfeny352aq4n

View File

@@ -0,0 +1,576 @@
extends GutInternalTester
class OverrideToString:
extends Node
func _to_string() -> String:
return "this has been overriden"
func foo():
pass
func before_all():
register_inner_classes(get_script())
func test_doubling_class_that_overrides_to_string_does_not_error():
var obj = double(OverrideToString).new()
obj.foo()
assert_engine_error_count(0)
func test_error_generated_when_attempting_to_stub_blacklisted_method():
var obj = double(OverrideToString).new()
stub(obj._to_string).to_return('hello')
assert_tracked_gut_error()
class TestDoubleCreation:
extends GutInternalTester
const TEMP_FILES = 'user://test_doubler_temp_file'
var _gut = null
var _test = null
func before_each():
_gut = new_gut(verbose)
_gut._should_print_versions = false
_test = new_wired_test(_gut)
add_child_autofree(_gut)
add_child_autofree(_test)
func after_each():
_gut.get_spy().clear()
func test_double_returns_a_class():
var D = _test.double(DoubleMe)
assert_ne(autofree(D.new()), null)
func test_double_sets_stubber_for_doubled_class():
var d = autofree(_test.double(DoubleMe).new())
assert_eq(d.__gutdbl.stubber_ref.get_ref(), _gut.get_stubber())
func test_basic_double_and_stub():
var d = autofree(_test.double(DoubleMe).new())
_test.stub(DOUBLE_ME_PATH, 'get_value').to_return(10)
assert_eq(d.get_value(), 10)
func test_get_set_double_strat():
assert_accessors(_test, 'double_strategy', _test.DOUBLE_STRATEGY.SCRIPT_ONLY, _test.DOUBLE_STRATEGY.INCLUDE_NATIVE)
func test_when_strategy_is_full_then_supers_are_spied():
var doubled = _test.double(DoubleMe, _test.DOUBLE_STRATEGY.INCLUDE_NATIVE).new()
autofree(doubled)
doubled.is_blocking_signals()
_test.assert_called(doubled, 'is_blocking_signals')
assert_eq(_test.get_pass_count(), 1)
func test_when_strategy_is_partial_then_spying_on_non_overloaded_fails():
var doubled = _test.double(DoubleMe, _test.DOUBLE_STRATEGY.SCRIPT_ONLY).new()
autofree(doubled)
doubled.is_blocking_signals()
_test.assert_not_called(doubled, 'is_blocking_signals')
assert_eq(_test.get_fail_count(), 1)
func test_can_override_strategy_when_doubling_scene():
var doubled = _test.double(DoubleMeScene, _test.DOUBLE_STRATEGY.INCLUDE_NATIVE).instantiate()
autofree(doubled)
doubled.is_blocking_signals()
_test.assert_called(doubled, 'is_blocking_signals')
assert_eq(_test.get_pass_count(), 1)
func test_when_strategy_is_partial_then_spying_on_non_overloaded_fails_with_scenes():
var doubled = _test.double(DoubleMeScene, _test.DOUBLE_STRATEGY.SCRIPT_ONLY).instantiate()
autofree(doubled)
doubled.is_blocking_signals()
_test.assert_not_called(doubled, 'is_blocking_signals')
assert_eq(_test.get_fail_count(), 1)
func test_can_stub_inner_class_methods():
_gut.get_doubler().inner_class_registry.register(InnerClasses)
var d = _gut.get_doubler().double(InnerClasses.InnerA).new()
_test.stub(InnerClasses.InnerA, 'get_a').to_return(10)
assert_eq(d.get_a(), 10)
func test_can_stub_multiple_inner_classes():
_gut.get_doubler().inner_class_registry.register(InnerClasses)
var a = _gut.get_doubler().double(InnerClasses.InnerA).new()
var anotherA = _gut.get_doubler().double(InnerClasses.AnotherInnerA).new()
_test.stub(a, 'get_a').to_return(10)
_test.stub(anotherA, 'get_a').to_return(20)
assert_eq(a.get_a(), 10)
assert_eq(anotherA.get_a(), 20)
func test_can_stub_multiple_inners_using_class_path_and_inner_names():
_test.register_inner_classes(InnerClasses)
var inner_a = _gut.get_doubler().double(InnerClasses.InnerA).new()
var another_a = _gut.get_doubler().double(InnerClasses.AnotherInnerA).new()
_test.stub(InnerClasses.InnerA, 'get_a').to_return(10)
assert_eq(inner_a.get_a(), 10, 'InnerA should be stubbed')
assert_eq(another_a.get_a(), null, 'AnotherA should NOT be stubbed')
if(is_failing()):
gut.p(_gut.get_stubber().to_s())
func test_when_stub_passed_a_non_doubled_instance_it_generates_an_error():
var n = autofree(Node.new())
_test.stub(n, 'something').to_return(3)
assert_tracked_gut_error(_test)
func test_when_stub_passed_singleton_it_generates_error():
_test.stub(Input, "is_action_just_pressed").to_return(true)
assert_tracked_gut_error(_test)
func test_can_stub_scenes():
var dbl_scn = _test.double(DoubleMeScene).instantiate()
autofree(dbl_scn)
_test.stub(dbl_scn, 'return_hello').to_return('world')
assert_eq(dbl_scn.return_hello(), 'world')
class TestSingletonDoubling:
extends GutInternalTester
var _gut = null
var _test = null
func before_each():
_gut = new_gut(verbose)
_test = new_wired_test(_gut)
add_child_autofree(_gut)
add_child_autofree(_test)
func test_double_singlton_creates_something_you_can_instantiate():
var D = _test.double_singleton(ResourceSaver)
var inst = D.new()
assert_not_null(inst)
func test_partial_double_singlton_creates_something_you_can_instantiate():
var D = _test.partial_double_singleton(NativeMenu)
var inst = D.new()
assert_not_null(inst)
func test_partial_double_singleton_returns_partial_double():
var D = _test.partial_double_singleton(NativeMenu)
var inst = D.new()
assert_true(inst.__gutdbl_values.is_partial)
func test_double_singleton_errors_when_not_in_whitelist():
var D = _test.double_singleton(GutTest)
assert_tracked_gut_error_text(_gut, "Use double")
assert_null(D)
func test_partial_double_singleton_errors_when_not_in_whitelist():
var D = _test.partial_double_singleton(GutTest)
assert_tracked_gut_error_text(_gut, "Use partial_double")
assert_null(D)
func test_double_errors_when_class_in_singleton_whitelist():
var D = _test.double(OS)
assert_tracked_gut_error_text(_gut, 'Use double_singleton')
assert_null(D)
func test_partial_double_errors_when_class_in_singleton_whitelist():
var D = _test.partial_double(Time)
assert_tracked_gut_error_text(_gut, 'Use partial_double_singleton')
assert_null(D)
func test_can_stub_double_based_on_parameters():
var dbl_time = _test.double_singleton(Time).new()
_test.stub(dbl_time.get_time_string_from_unix_time.bind(1))\
.to_return("one")
_test.stub(dbl_time.get_time_string_from_unix_time.bind(2))\
.to_return("two")
assert_eq(dbl_time.get_time_string_from_unix_time(1), "one")
assert_eq(dbl_time.get_time_string_from_unix_time(2), "two")
assert_null(dbl_time.get_time_string_from_unix_time(3), 'nonstubbed value returns null')
func test_do_not_have_to_specify_defaulted_vaules_for_stub_to_match():
var dbl_input = _test.double_singleton(Input).new()
_test.stub(dbl_input.is_action_just_pressed.bind("jump"))\
.to_return(true)
_test.assert_true(dbl_input.is_action_just_pressed("jump"))
_test.assert_called(dbl_input.is_action_just_pressed.bind("jump", false))
assert_pass(_test, 2)
func test_can_stub_methods_with_default_values():
var dbl_input = _test.double_singleton(Input).new()
_test.stub(dbl_input.is_action_just_pressed.bind("jump", false))\
.to_return(true)
assert_true(dbl_input.is_action_just_pressed("jump", false))
_test.assert_called(dbl_input.is_action_just_pressed.bind("jump", false))
assert_pass(_test)
class TestIgnoreMethodsWhenDoubling:
extends GutInternalTester
var _gut = null
var _test = null
func before_each():
_gut = new_gut(verbose)
_test = new_wired_test(_gut)
add_child_autofree(_gut)
add_child_autofree(_test)
func test_sends_loaded_script_to_the_doubler():
var m_doubler = double(GutUtils.Doubler).new()
_gut._doubler = m_doubler
_test.ignore_method_when_doubling(DoubleMe, 'two')
assert_called(m_doubler, 'add_ignored_method', [DoubleMe, 'two'])
func test_sends_loaded_scene_to_the_doubler():
var m_doubler = double(GutUtils.Doubler).new()
_gut._doubler = m_doubler
_test.ignore_method_when_doubling(DoubleMeScene, 'two')
assert_called(m_doubler, 'add_ignored_method',
[GutUtils.get_scene_script_object(DoubleMeScene), 'two'])
func test_when_ignoring_scene_methods_they_are_not_doubled():
_test.ignore_method_when_doubling(DoubleMeScene, 'return_hello')
var m_inst = _test.double(DoubleMeScene).instantiate()
autofree(m_inst)
m_inst.return_hello()
# since it is ignored it should not have been caught by the stubber
_test.assert_not_called(m_inst, 'return_hello')
assert_eq(_test.get_fail_count(), 1)
func test_when_ignoring_sigleton_methods_they_do_not_exist_in_doubles():
_test.ignore_method_when_doubling(Time, 'get_ticks_msec')
var inst = _test.double_singleton(Time).new()
assert_false(inst.has_method('get_ticks_msec'))
class TestTestsSmartDoubleMethod:
extends GutInternalTester
var _gut = null
var _test = null
func before_all():
_test = Test.new()
_test.gut = gut
_test.set_logger(gut.logger)
func after_all():
_test.queue_free()
func after_each():
gut.get_stubber().clear()
func test_when_passed_a_script_it_doubles_script():
var inst = _test.double(DoubleMe).new()
assert_eq(inst.__gutdbl.thepath, DOUBLE_ME_PATH)
func test_when_passed_a_scene_it_doubles_a_scene():
var inst = _test.double(DoubleMeScene).instantiate()
assert_eq(inst.__gutdbl.thepath, DOUBLE_ME_SCENE_PATH)
func test_doulbing_inners_with_objects():
_test.register_inner_classes(InnerClasses)
var inst = _test.double(InnerClasses.InnerA).new()
assert_eq(inst.__gutdbl.thepath, INNER_CLASSES_PATH, 'check path')
assert_eq(inst.__gutdbl.subpath, 'InnerA', 'check subpath')
func test_include_native_strategy_used_for_scripts():
var inst = _test.double(DoubleMe, DOUBLE_STRATEGY.INCLUDE_NATIVE).new()
inst.get_instance_id()
assert_called(inst, 'get_instance_id')
func test_script_only_strategy_used_for_scripts():
var inst = _test.double(DoubleMe, DOUBLE_STRATEGY.SCRIPT_ONLY).new()
assert_does_not_have(inst.__gutdbl_values.doubled_methods, 'get_instance_id')
func test_include_native_strategy_used_with_scenes():
var inst = _test.double(DoubleMeScene, DOUBLE_STRATEGY.INCLUDE_NATIVE).instantiate()
assert_has(inst.__gutdbl_values.doubled_methods, 'get_instance_id')
func test_script_ony_strategy_used_with_scenes():
var inst = _test.double(DoubleMeScene, DOUBLE_STRATEGY.SCRIPT_ONLY).instantiate()
assert_does_not_have(inst.__gutdbl_values.doubled_methods, 'get_instance_id')
func test_include_native_strategy_used_with_inners():
_test.register_inner_classes(InnerClasses)
var inst = _test.double(InnerClasses.InnerA, DOUBLE_STRATEGY.INCLUDE_NATIVE).new()
assert_has(inst.__gutdbl_values.doubled_methods, 'get_instance_id')
func test_script_only_strategy_used_with_inners():
_test.register_inner_classes(InnerClasses)
var inst = _test.double(InnerClasses.InnerA, DOUBLE_STRATEGY.SCRIPT_ONLY).new()
assert_does_not_have(inst.__gutdbl_values.doubled_methods, 'get_instance_id')
func test_when_passing_a_class_of_a_script_it_doubles_it():
var inst = _test.double(DoubleMe).new()
assert_eq(inst.__gutdbl.thepath, DOUBLE_ME_PATH)
func test_when_passing_a_class_of_a_scene_it_doubles_it():
var inst = _test.double(DoubleMeScene).instantiate()
assert_eq(inst.__gutdbl.thepath, DOUBLE_ME_SCENE_PATH)
func test_can_double_native_classes():
var inst = _test.double(Node2D).new()
assert_not_null(inst)
func test_doubled_natives_call_super_by_default():
var inst = _test.double(Node2D).new()
assert_not_null(inst.get_position())
func test_when_an_instance_is_passed_null_is_returned_and_an_error_is_generated():
var inst = autofree(Node2D.new())
var d = _test.double(inst)
assert_null(d, 'double is null')
assert_tracked_gut_error(_test)
class TestPartialDoubleMethod:
extends GutInternalTester
var _gut = null
var _test = null
func before_all():
_gut = new_gut(verbose)
_test = new_wired_test(_gut)
add_child(_gut)
add_child(_test)
func after_each():
_gut.get_stubber().clear()
func after_all():
_gut.free()
_test.free()
func test_partial_double_script():
var inst = _test.partial_double(DoubleMe).new()
autofree(inst)
inst.set_value(10)
assert_eq(inst.get_value(), 10)
func test_partial_double_scene():
var inst = _test.partial_double(DoubleMeScene).instantiate()
autofree(inst)
assert_eq(inst.return_hello(), 'hello', 'sometimes fails, should be fixed.')
func test_partial_double_inner():
_test.register_inner_classes(InnerClasses)
var inst = _test.partial_double(InnerClasses.InnerA).new()
assert_eq(inst.get_a(), 'a')
func test_double_script_not_a_partial():
var inst = _test.double(DoubleMe).new()
autofree(inst)
inst.set_value(10)
assert_eq(inst.get_value(), null)
func test_double_scene_not_a_partial():
var inst = _test.double(DoubleMeScene).instantiate()
autofree(inst)
assert_eq(inst.return_hello(), null)
func test_double_inner_not_a_partial():
_test.register_inner_classes(InnerClasses)
var inst = _test.double(InnerClasses.InnerA).new()
assert_eq(inst.get_a(), null)
func test_can_spy_on_partial_doubles():
var pass_count = _test.get_pass_count()
var inst = _test.partial_double(DoubleMe).new()
autofree(inst)
inst.set_value(10)
_test.assert_called(inst, 'set_value')
_test.assert_called(inst, 'set_value', [10])
assert_eq(_test.get_pass_count(), pass_count + 2)
func test_can_stub_partial_doubled_native_class():
var inst = _test.partial_double(Node2D).new()
autofree(inst)
_test.stub(inst, 'get_position').to_return(-1)
assert_eq(inst.get_position(), -1)
func test_can_stub_partial_doubled_native_class_to_do_nothing_before_creating_double():
_test.stub(Node2D, 'get_position').to_do_nothing()
var inst = _test.partial_double(Node2D).new()
autofree(inst)
assert_eq(inst.get_position(), null)
func test_can_stub_partial_doubled_native_class_to_do_nothing_after_creating_double():
var inst = _test.partial_double(Node2D).new()
_test.stub(Node2D, 'get_position').to_do_nothing()
autofree(inst)
assert_eq(inst.get_position(), null)
func test_can_spy_on_partial_doubled_native_class():
var pass_count = _test.get_pass_count()
var inst = autofree(_test.partial_double(Node2D).new())
inst.set_position(Vector2(100, 100))
_test.assert_called(inst, 'set_position', [Vector2(100, 100)])
assert_eq(_test.get_pass_count(), pass_count + 1, 'tests have passed')
func test_when_an_instance_is_passed_null_is_returned_and_an_error_is_generated():
var inst = autofree(Node2D.new())
var d = _test.partial_double(inst)
assert_null(d, 'double is null')
assert_tracked_gut_error(_test)
func test_can_override_partial_double_stubs():
var inst = _test.partial_double(DoubleMe).new()
autofree(inst)
_test.stub(DoubleMe, 'get_value').to_do_nothing()
inst.set_value(10)
assert_null(inst.get_value())
class TestOverridingParameters:
extends GutInternalTester
var _gut = null
var _test = null
const INIT_PARAMETERS = 'res://test/resources/stub_test_objects/init_parameters.gd'
const DEFAULT_PARAMS_PATH = 'res://test/resources/doubler_test_objects/double_default_parameters.gd'
var DefaultParams = null
func before_all():
var were_set = GutUtils.WarningsManager.are_warnings_enabled()
GutUtils.WarningsManager.enable_warnings(false)
DefaultParams = load(DEFAULT_PARAMS_PATH)
GutUtils.WarningsManager.enable_warnings(were_set)
func before_each():
_gut = new_gut(verbose)
_test = new_wired_test(_gut)
add_child(_gut)
add_child(_test)
func after_each():
_gut.free()
_test.free()
# -------------------
# Default parameters and override parameter count
func test_can_stub_default_values():
var TestClass = load(DEFAULT_PARAMS_PATH)
var s = _test.stub(TestClass, 'return_passed').to_call_super()
s.param_defaults(['1', '2'])
var inst = _test.double(DefaultParams).new()
var ret_val = inst.return_passed()
assert_eq(ret_val, '12')
func test_vararg_methods_get_extra_parameters_by_default():
_test.stub(Node, 'rpc_id').to_do_nothing()
var inst = _test.double(Node).new()
add_child_autofree(inst)
var ret_val = inst.rpc_id(1, 'foo', '3', '4', '5')
pass_test('we got here')
func test_issue_246_rpc_id_varargs():
var inst = _test.double(Node).new()
_test.stub(Node, 'rpc_id').to_do_nothing()
add_child_autofree(inst)
inst.rpc_id(1, 'foo', '3', '4', '5')
_test.assert_called(inst, 'rpc_id', [1, 'foo', ['3', '4', '5']])
assert_eq(_test.get_pass_count(), 1)
func test_issue_246_rpc_id_varargs2():
stub(Node, 'rpc_id').to_do_nothing()
var inst = double(Node).new()
add_child_autofree(inst)
inst.rpc_id(1, 'foo', '3', '4', '5')
assert_called(inst, 'rpc_id', [1, 'foo', ['3', '4', '5']])
func test_double_can_have_default_param_values_stubbed_using_class():
var InitParams = load(INIT_PARAMETERS)
_test.stub(InitParams, '_init').param_defaults(["override_default"])
var inst = _test.double(InitParams).new()
assert_eq(inst.value, 'override_default')
class TestStub:
extends GutInternalTester
var _gut = null
var _test = null
func before_each():
_gut = new_gut(verbose)
_test = new_wired_test(_gut)
_test.ignore_method_when_doubling(DoubleMe, '_notification')
add_child_autofree(_gut)
add_child_autofree(_test)
func after_each():
_gut.get_stubber().clear()
func test_stub_of_valid_stuff_is_fine():
var dbl = autofree(_test.double(DoubleMe).new())
_test.stub(dbl, 'get_value').to_return(9)
assert_tracked_gut_error(_test, 0)
func test_stub_of_double_method_generates_error_when_method_does_not_exist():
var dbl = autofree(_test.double(DoubleMe).new())
_test.stub(dbl, 'foo').to_do_nothing()
assert_tracked_gut_error(_test)
func test_can_stub_double_method_using_callable():
var d = autofree(_test.double(DoubleMe).new())
_test.stub(d.has_one_param).to_return(5)
assert_eq(_gut.get_stubber().get_return(d, 'has_one_param'), 5)
func test_errors_on_p2_when_using_callable():
var d = autofree(_test.double(DoubleMe).new())
_test.stub(d.has_one_param, 'asdf').to_return(5)
assert_tracked_gut_error(_test)
func test_errors_on_p3_when_using_callable():
var d = autofree(_test.double(DoubleMe).new())
_test.stub(d.has_one_param, null, 'asdf').to_return(5)
assert_tracked_gut_error(_test)
func test_bound_parameters_are_not_spied_on():
var d = autofree(_test.double(DoubleMe).new())
var callable = func(_value, p2):
return p2
_test.stub(d.has_one_param).to_call(callable.bind("p2"))
d.has_one_param("value")
_test.assert_not_called(d, "has_one_param", ["value", "p2"])
_test.assert_called(d, "has_one_param", ["value"])
assert_pass(_test, 2)
func test_setting_local_variable_in_callable():
var d = autofree(_test.double(DoubleMe).new())
var this_var = "some value"
_test.stub(d.has_one_param).to_call(
func(_value):
this_var = "another value"
return this_var)
var result = d.has_one_param("asdf")
_test.assert_eq(result, "another value", "Seems reasonable")
_test.assert_ne(result, this_var, "Why would this pass?")
_test.assert_eq(this_var, "some value", "Ohhh, well ok.")
assert_pass(_test, 3)
func test_get_error_messages_when_using_callables():
_test.ignore_method_when_doubling(DoubleMe, "has_one_param")
var d = autofree(_test.double(DoubleMe).new())
_test.stub(d.has_one_param).to_return(5)
assert_tracked_gut_error(_test)

View File

@@ -0,0 +1 @@
uid://cx61yixf4sd1s

View File

@@ -0,0 +1,4 @@
extends "res://addons/gut/test.gd"
func test_nothing():
pass_test('do not need a test, but felt weird to not have one.')

View File

@@ -0,0 +1 @@
uid://dhbs1hnc7hfqg

View File

@@ -0,0 +1,31 @@
extends GutTest
var UpdateDetector = GutUtils.UpdateDetector
var data = GutUtils.get_file_as_text('res://addons/gut/versions.json')
var ud = null
func before_all():
ud = UpdateDetector.new()
add_child(ud)
func after_all():
ud.free()
func test_local_versions_file_is_valid():
ud.parse_file(ud.LOCAL_FILE_PATH)
assert_eq(ud.data_issues.size(), 0, "no data issues")
if(is_failing()):
gut.p(str("Issues:\n", ud.data_issues))
assert_has(ud.parsed_data.releases, GutUtils.version_numbers.gut_version,
"current version exists")
func test_parsing_remote_data():
var error = ud.fetch_remote_file()
if(error == OK):
await wait_for_signal(ud.download_completed, 5)
assert_has(ud.parsed_data, 'releases')
else:
fail_test("There was an error starting request")

View File

@@ -0,0 +1 @@
uid://bk4enti2876q8