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,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)