extends GutTest class BaseTest: extends GutInternalTester const DOUBLE_WITH_STATIC = 'res://test/resources/doubler_test_objects/has_static_method.gd' var DoubleWithStatic = load(DOUBLE_WITH_STATIC) var Doubler = load('res://addons/gut/doubler.gd') var print_source_when_failing = true func get_source(thing): var to_return = null if(GutUtils.is_instance(thing)): to_return = thing.get_script().get_source_code() else: to_return = thing.source_code return to_return func assert_source_contains(thing, look_for, text=''): var source = get_source(thing) var msg = str('Expected source for ', str(thing), ' to contain "', look_for, '": ', text) if(source == null || source.find(look_for) == -1): fail_test(msg) if(print_source_when_failing): var header = str('------ Source for ', _strutils.type2str(thing), ' ------') gut.p(header) gut.p(GutUtils.add_line_numbers(source)) else: pass_test(msg) func assert_source_not_contains(thing, look_for, text=''): var source = get_source(thing) var msg = str('Expected source for ', _strutils.type2str(thing), ' to not contain "', look_for, '": ', text) if(source == null || source.find(look_for) == -1): pass_test(msg) else: fail_test(msg) if(print_source_when_failing): var header = str('------ Source for ', _strutils.type2str(thing), ' ------') gut.p(header) gut.p(GutUtils.add_line_numbers(source)) func print_source(thing): var source = get_source(thing) gut.p(GutUtils.add_line_numbers(source)) class TestTheBasics: extends BaseTest var _doubler = null var stubber = GutUtils.Stubber.new() func before_each(): stubber.clear() _doubler = Doubler.new() _doubler.set_stubber(stubber) _doubler.set_gut(gut) _doubler.set_strategy(DOUBLE_STRATEGY.SCRIPT_ONLY) var lgr = GutUtils.GutLogger.new() lgr.set_gut(gut) _doubler.set_logger(lgr) _doubler.print_source = false func test_get_set_stubber(): var dblr = Doubler.new() var default_stubber = dblr.get_stubber() assert_accessors(dblr, 'stubber', default_stubber, GutUtils.Stubber.new()) func test_can_get_set_spy(): assert_accessors(Doubler.new(), 'spy', null, GDScript.new()) func test_get_set_gut(): assert_accessors(Doubler.new(), 'gut', null, GDScript.new()) func test_get_set_logger(): assert_ne(_doubler.get_logger(), null) var l = load('res://addons/gut/logger.gd').new() _doubler.set_logger(l) assert_eq(_doubler.get_logger(), l) func test_doubler_sets_logger_of_method_maker(): assert_eq(_doubler.get_logger(), _doubler._method_maker.get_logger()) func test_setting_logger_sets_it_on_method_maker(): var l = load('res://addons/gut/logger.gd').new() _doubler.set_logger(l) assert_eq(_doubler.get_logger(), _doubler._method_maker.get_logger()) func test_get_set_strategy(): assert_accessors(_doubler, 'strategy', GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY, GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE) func test_cannot_set_strategy_to_invalid_values(): var default = _doubler.get_strategy() _doubler.set_strategy(-1) assert_tracked_gut_error() assert_eq(_doubler.get_strategy(), default, 'original value retained') func test_can_set_strategy_in_constructor(): var d = Doubler.new(GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE) assert_eq(d.get_strategy(), GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE) class TestDoublingScripts: extends BaseTest var _doubler = null var stubber = GutUtils.Stubber.new() func before_each(): stubber.clear() _doubler = Doubler.new() _doubler.set_stubber(stubber) _doubler.set_gut(gut) func test_doubling_object_includes_methods(): var inst = _doubler.double(DoubleMe) inst = inst.new() assert_source_contains(inst, 'func get_value(') assert_source_contains(inst, 'func set_value(') func test_doubling_methods_have_parameters_1(): var inst = _doubler.double(DoubleMe).new() assert_source_contains(inst, 'has_one_param(p_one=', 'first parameter for one param method is defined') # Don't see a way to see which have defaults and which do not, so we default # everything. func test_all_parameters_are_defaulted_to_null(): var inst = _doubler.double(DoubleMe).new() assert_source_contains(inst, 'has_two_params_one_default(' + 'p_one=__gutdbl.default_val("has_two_params_one_default",0), '+ 'p_two=__gutdbl.default_val("has_two_params_one_default",1))') # assert_true(text.match('*has_two_params_one_default(p_arg0=__gut_default_val("has_two_params_one_default",0), p_arg1=__gut_default_val("has_two_params_one_default",1))*')) func test_doubled_thing_includes_stubber_metadata(): var doubled = _doubler.double(DoubleMe).new() assert_ne(doubled.get('__gutdbl'), null) func test_doubled_thing_has_original_path_in_metadata(): var doubled = _doubler.double(DoubleMe).new() assert_eq(doubled.__gutdbl.thepath, DOUBLE_ME_PATH) func test_doublecd_thing_has_gut_metadata(): var doubled = _doubler.double(DoubleMe).new() assert_eq(doubled.__gutdbl.gut_ref.get_ref(), gut) func test_keeps_extends(): var doubled = _doubler.double(DoubleExtendsNode2D).new() assert_is(doubled, Node2D) func test_does_not_add_duplicate_methods(): var TheClass = load('res://test/resources/parsing_and_loading_samples/extends_another_thing.gd') _doubler.double(TheClass) assert_true(true, 'If we get here then the duplicates were removed.') func test_returns_class_that_can_be_instanced(): var Doubled = _doubler.double(DoubleMe) var doubled = Doubled.new() assert_ne(doubled, null) func test_doubles_retain_signals(): var d = _doubler.double(DOUBLE_ME_PATH).new() assert_has_signal(d, 'signal_signal') func test_double_includes_list_of_doubled_methods(): var d = _doubler.double(DOUBLE_ME_PATH).new() assert_ne(d.__gutdbl_values.doubled_methods.size(), 0) func test_doubled_methods_includes_overloaded_methods(): var d = _doubler.double(DOUBLE_ME_PATH).new() assert_has(d.__gutdbl_values.doubled_methods, '_ready') func test_doubled_methods_includes_script_methods(): var d = _doubler.double(DOUBLE_ME_PATH).new() assert_has(d.__gutdbl_values.doubled_methods, 'might_await') func test_doubled_methods_does_not_included_non_overloaded_methods(): var d = _doubler.double(DOUBLE_ME_PATH).new() assert_does_not_have(d.__gutdbl_values.doubled_methods, '_input') class TestAddingIgnoredMethods: extends BaseTest var _doubler = null var stubber = GutUtils.Stubber.new() func before_each(): stubber.clear() _doubler = Doubler.new() _doubler.set_stubber(stubber) _doubler.set_gut(gut) _doubler.print_source = false func test_can_add_to_ignore_list(): assert_eq(_doubler.get_ignored_methods().size(), 0, 'initial size') _doubler.add_ignored_method(DoubleWithStatic, 'some_method') assert_eq(_doubler.get_ignored_methods().size(), 1, 'after add') func test_when_ignored_methods_are_a_local_method_mthey_are_not_present_in_double_code(): _doubler.add_ignored_method(DoubleMe, 'has_one_param') var c = _doubler.double(DoubleMe) assert_source_not_contains(c.new(), 'has_one_param') func test_when_ignored_methods_are_a_super_method_they_are_not_present_in_double_code(): _doubler.add_ignored_method(DoubleMe, 'is_connected') var c = _doubler.double(DoubleMe, GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE) assert_source_not_contains(c.new(), 'is_connected') func test_can_double_classes_with_static_methods(): _doubler.add_ignored_method(DoubleWithStatic, 'this_is_a_static_method') var d = _doubler.double(DoubleWithStatic).new() assert_null(d.this_is_not_static()) class TestDoubleScene: extends BaseTest var _doubler = null var stubber = GutUtils.Stubber.new() func before_each(): stubber.clear() _doubler = Doubler.new() _doubler.set_stubber(stubber) _doubler.set_gut(gut) _doubler.print_source = false func test_can_double_scene(): var obj = _doubler.double_scene(DoubleMeScene) var inst = obj.instantiate() assert_eq(inst.return_hello(), null) func test_can_add_doubled_scene_to_tree(): var inst = _doubler.double_scene(DoubleMeScene).instantiate() add_child(inst) assert_ne(inst.label, null) remove_child(inst) func test_metadata_for_scenes_script_points_to_scene_not_script(): var inst = _doubler.double_scene(DoubleMeScene).instantiate() assert_eq(inst.__gutdbl.thepath, DOUBLE_ME_SCENE_PATH) func test_can_override_strategy_when_doubling_scene(): _doubler.set_strategy(GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY) var inst = autofree(_doubler.double_scene(DoubleMeScene, GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE).instantiate()) assert_source_contains(inst, 'func is_blocking_signals') func test_full_start_has_block_signals(): _doubler.set_strategy(GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE) var inst = autofree(_doubler.double_scene(DoubleMeScene).instantiate()) assert_source_contains(inst, 'func is_blocking_signals') class TestDoubleStrategyIncludeNative: extends BaseTest func _hide_call_back(): pass var doubler = null var stubber = GutUtils.Stubber.new() func before_each(): stubber.clear() doubler = Doubler.new(GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE) doubler.set_stubber(stubber) doubler.print_source = false func test_built_in_overloading_ony_happens_on_full_strategy(): doubler.set_strategy(GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY) var inst = autofree(doubler.double(DoubleMe).new()) var txt = get_source(inst) assert_false(txt == '', "text is not empty") assert_source_not_contains(inst, 'func is_blocking_signals', 'does not have non-overloaded methods') func test_can_override_strategy_when_doubling_script(): doubler.set_strategy(GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY) var inst = doubler.double(DoubleMe, GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE).new() autofree(inst) assert_source_contains(inst, 'func is_blocking_signals') func test_when_everything_included_you_can_still_make_an_a_new_object(): var inst = autofree(doubler.double(DoubleMe).new()) assert_ne(inst, null) func test_when_everything_included_you_can_still_make_a_new_node2d(): var inst = autofree(doubler.double(DoubleExtendsNode2D).new()) assert_ne(inst, null) func test_when_everything_included_you_can_still_double_a_scene(): var inst = autofree(doubler.double_scene(DoubleMeScene).instantiate()) add_child(inst) assert_ne(inst, null, "instantiate is not null") assert_ne(inst.label, null, "Can get to a label on the instantiate") # pause so _process gets called await wait_seconds(3) func test_double_includes_methods_in_super(): var inst = autofree(doubler.double(DoubleExtendsWindowDialog).new()) assert_source_contains(inst, 'connect(') func test_can_call_a_built_in_that_has_default_parameters(): var inst = autofree(doubler.double(DoubleExtendsWindowDialog).new()) inst.size_changed.connect(self._hide_call_back) pass_test("if we got here, it worked") func test_doubled_builtins_call_super(): var inst = autofree(doubler.partial_double(DoubleExtendsWindowDialog).new()) # Make sure the function is in the doubled class definition assert_source_contains(inst, 'func add_user_signal(p_signal') # Make sure that when called it retains old functionality. inst.add_user_signal('new_one', []) inst.add_user_signal('new_two', ['a', 'b']) assert_has_signal(inst, 'new_one') assert_has_signal(inst, 'new_two') func test_doubled_builtins_are_added_as_stubs_to_call_super(): var inst = autofree(doubler.partial_double(DoubleExtendsWindowDialog).new()) assert_true(doubler.get_stubber().should_call_super(inst, 'add_user_signal')) func test_doubled_methods_includes_non_overloaded_methods(): var inst = autofree(doubler.double(DoubleMe).new()) assert_has(inst.__gutdbl_values.doubled_methods, 'get_parent') func test_doubled_methods_does_not_included_non_overloaded_virtual_methods(): var inst = autofree(doubler.double(DoubleMe).new()) assert_does_not_have(inst.__gutdbl_values.doubled_methods, '_input') class TestPartialDoubles: extends BaseTest var doubler = null var stubber = GutUtils.Stubber.new() func before_each(): stubber.clear() doubler = Doubler.new() doubler.set_stubber(stubber) func test_can_make_partial_of_script(): var inst = autofree(doubler.partial_double(DoubleMe).new()) inst.set_value(10) assert_eq(inst.get_value(), 10) func test_double_script_does_not_make_partials(): var inst = autofree(doubler.double(DoubleMe).new()) assert_eq(inst.get_value(), null) func test_can_make_partial_of_scene(): var inst = autofree(doubler.partial_double_scene(DoubleMeScene).instantiate()) assert_eq(inst.return_hello(), 'hello') func test_double_scene_does_not_call_supers(): var inst = autofree(doubler.double_scene(DoubleMeScene).instantiate()) assert_eq(inst.return_hello(), null) func test_init_is_not_stubbed_to_call_super(): var inst = autofree(doubler.partial_double(DoubleMe).new()) var text = get_source(inst) assert_false(text.match("*__gutdbl.should_call_super('_init'*"), 'should not call super _init') func test_can_partial_and_normal_double_in_same_test(): var dbl = autofree(doubler.double(DoubleMe).new()) var p_double = autofree(doubler.partial_double(DoubleMe).new()) assert_null(dbl.get_value(), 'double get_value') assert_eq(p_double.get_value(), 0, 'partial get_value') if(is_failing()): print(doubler.get_stubber().to_s()) class TestDoubleGDNaviteClasses: extends BaseTest var _doubler = null var _stubber = GutUtils.Stubber.new() func before_each(): _stubber.clear() _doubler = Doubler.new() _doubler.set_stubber(_stubber) _doubler.print_source = false # --------- # Note: Orphans in these tests are caused by the script parser holding onto # the native instances it creates. These are released when the script parser # is unreferenced. See test_script_parser.gd for more info. Decided not to # fight the orhpans here. # --------- func test_can_double_Node2D(): var d_node_2d = _doubler.double_gdnative(Node2D) assert_not_null(d_node_2d) func test_can_partial_double_Node2D(): var pd_node_2d = _doubler.partial_double_gdnative(Node2D) assert_not_null(pd_node_2d) func test_can_make_instances_of_native_doubles(): var crect_double_inst = _doubler.double_gdnative(ColorRect).new() autofree(crect_double_inst) assert_not_null(crect_double_inst) func test_can_make_double_of_ref_counted_native(): var dbl = _doubler.double_gdnative(StreamPeerTCP) assert_not_null(dbl) class TestDoubleInnerClasses: extends BaseTest var doubler = null func before_each(): doubler = Doubler.new() doubler.set_stubber(GutUtils.Stubber.new()) var lgr = GutUtils.GutLogger.new() lgr.set_gut(gut) doubler.set_logger(lgr) func test_when_inner_class_not_registered_it_generates_error(): doubler.double(InnerClasses.InnerA) assert_tracked_gut_error() func test_when_inner_class_registered_it_makes_a_double(): doubler.inner_class_registry.register(InnerClasses) var Dbl = doubler.double(InnerClasses.InnerA) assert_tracked_gut_error(doubler, 0) assert_not_null(Dbl, 'made a double') func test_doubled_instance_returns_null_for_get_b1(): doubler.inner_class_registry.register(InnerClasses) var dbld = doubler.double(InnerClasses.InnerB.InnerB1).new() assert_null(dbld.get_b1()) func test_can_make_an_instance_of_a_double_of_a_registered_inner_class(): doubler.inner_class_registry.register(InnerClasses) var inst = doubler.double(InnerClasses.InnerB.InnerB1).new() assert_not_null(inst, 'inst is not null') assert_is(inst, InnerClasses.InnerB.InnerB1) func test_doubled_inners_that_extend_inners_get_full_inheritance(): doubler.inner_class_registry.register(InnerClasses) var inst = doubler.double(InnerClasses.InnerCA).new() assert_has_method(inst, 'get_a') assert_has_method(inst, 'get_ca') func test_doubled_inners_have_subpath_set_in_metadata(): doubler.inner_class_registry.register(InnerClasses) var inst = doubler.double(InnerClasses.InnerCA).new() assert_eq(inst.__gutdbl.subpath, 'InnerCA') func test_non_inners_have_empty_subpath(): var inst = doubler.double(InnerClasses).new() assert_eq(inst.__gutdbl.subpath, '') func test_can_override_strategy_when_doubling(): doubler.inner_class_registry.register(InnerClasses) var d = doubler.double(InnerClasses.InnerA, DOUBLE_STRATEGY.INCLUDE_NATIVE) # make sure it has something from Object that isn't implemented assert_source_contains(d.new() , 'func disconnect(p_signal') assert_eq(doubler.get_strategy(), DOUBLE_STRATEGY.SCRIPT_ONLY, 'strategy should have been reset') func test_doubled_inners_retain_signals(): doubler.inner_class_registry.register(InnerClasses) var inst = doubler.double(InnerClasses.InnerWithSignals).new() assert_has_signal(inst, 'signal_signal') func test_double_inner_does_not_call_supers(): doubler.inner_class_registry.register(InnerClasses) var inst = doubler.double(InnerClasses.InnerA).new() assert_eq(inst.get_a(), null) func test_can_make_partial_of_inner_script(): doubler.inner_class_registry.register(InnerClasses) var inst = doubler.partial_double(InnerClasses.InnerA).new() assert_eq(inst.get_a(), 'a') func test_partial_double_errors_if_inner_not_registered(): doubler.partial_double(InnerClasses.InnerA) assert_tracked_gut_error() class TestAutofree: extends BaseTest class InitHasDefaultParams: var a = 'b' func _init(value='asdf'): a = value func test_doubles_are_autofreed(): var doubled = double(DoubleExtendsNode2D).new() gut.get_autofree().free_all() assert_no_new_orphans() func test_partial_doubles_are_autofreed(): var doubled = partial_double(DoubleExtendsNode2D).new() gut.get_autofree().free_all() assert_no_new_orphans() class TestDoubleSingletons: extends BaseTest var doubler = null func before_each(): doubler = Doubler.new() doubler.set_stubber(GutUtils.Stubber.new()) var lgr = GutUtils.GutLogger.new() lgr.set_gut(gut) doubler.set_logger(lgr) func test_can_make_a_double_of_OS(): var d = doubler.double_singleton(Input) assert_not_null(d) func test_doubles_are_ref_counted(): var d = doubler.double_singleton(Time) var inst = d.new() assert_is(inst, RefCounted) func test_adds_gut_data(): var d = doubler.double_singleton(OS) var inst = d.new() assert_typeof(inst.__gutdbl_values, TYPE_DICTIONARY) assert_has_method(inst, '__gutdbl_check_method__') assert_not_null(inst.__gutdbl) assert_has_method(inst, '__gutdbl_done') func test_populates_gut_data_singleton_values(): var d = doubler.double_singleton(OS) var inst = d.new() assert_eq(inst.__gutdbl_values.singleton_name, "OS") assert_eq(inst.__gutdbl_values.singleton, OS.get_instance_id(), 'singleton instance id') func test_double_has_singleton_methods(): var d = doubler.double_singleton(OS) var inst = d.new() assert_has_method(inst, 'get_data_dir') func test_double_has_enums(): var inst = doubler.double_singleton(Time).new() assert_eq(inst.MONTH_JANUARY, 1) func test_double_has_signal(): var inst = doubler.double_singleton(AudioServer).new() assert_has_signal(inst, 'bus_layout_changed') var _on_bus_renamed_bus_index = 999 var _on_bus_renamed_old_name = '' var _on_bus_renamed_new_name = '' func _on_bus_renamed(bus_index, old_name, new_name): _on_bus_renamed_bus_index = bus_index _on_bus_renamed_old_name = old_name _on_bus_renamed_new_name = new_name func test_can_connect_to_signal_with_parameters(): var inst = doubler.double_singleton(AudioServer).new() inst.bus_renamed.connect(_on_bus_renamed) assert_connected(inst.bus_renamed, self) inst.bus_renamed.emit(-1, 'old', 'new') assert_eq(_on_bus_renamed_bus_index, -1) assert_eq(_on_bus_renamed_old_name, 'old') assert_eq(_on_bus_renamed_new_name, 'new') func test_singltons_contain_properties(): var inst = doubler.double_singleton(OS).new() assert_true(inst.delta_smoothing) assert_false(inst.low_processor_usage_mode) func test_can_double_all_singletons(p = use_parameters(GutUtils.GodotSingletons.class_ref)): var D = doubler.double_singleton(p) assert_not_null(D, 'singleton: ' + p.get_class()) if(is_passing()): assert_not_null(D.new(), 'instance') func test_can_make_partial_double_of_singleton(): var D = doubler.partial_double_singleton(Time) assert_not_null(D) func test_partial_double_singletons_call_super(): var D = doubler.partial_double_singleton(Time) var inst = D.new() assert_not_null(inst.get_ticks_msec()) func test_singleton_doubles_have_doubled_methods_populated(): var inst = doubler.double_singleton(Performance).new() assert_ne(inst.__gutdbl_values.doubled_methods.size(), 0) func test_singleton_doubles_do_not_have_object_methods(): var inst = doubler.double_singleton(Performance).new() assert_does_not_have(inst.__gutdbl_values.doubled_methods, 'get_property_list') func test_singleton_doubles_have_singleton_methods(): var inst = doubler.double_singleton(Performance).new() assert_has(inst.__gutdbl_values.doubled_methods, 'get_custom_monitor_names')