diff options
Diffstat (limited to 'addons/gut/doubler.gd')
| -rw-r--r-- | addons/gut/doubler.gd | 751 |
1 files changed, 0 insertions, 751 deletions
diff --git a/addons/gut/doubler.gd b/addons/gut/doubler.gd deleted file mode 100644 index ce865e5..0000000 --- a/addons/gut/doubler.gd +++ /dev/null @@ -1,751 +0,0 @@ -# ############################################################################## -#(G)odot (U)nit (T)est class -# -# ############################################################################## -# The MIT License (MIT) -# ===================== -# -# Copyright (c) 2020 Tom "Butch" Wesley -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# ############################################################################## -# Description -# ----------- -# ############################################################################## - -# ------------------------------------------------------------------------------ -# Utility class to hold the local and built in methods separately. Add all local -# methods FIRST, then add built ins. -# ------------------------------------------------------------------------------ -class ScriptMethods: - # List of methods that should not be overloaded when they are not defined - # in the class being doubled. These either break things if they are - # overloaded or do not have a "super" equivalent so we can't just pass - # through. - var _blacklist = [ - 'has_method', - 'get_script', - 'get', - '_notification', - 'get_path', - '_enter_tree', - '_exit_tree', - '_process', - '_draw', - '_physics_process', - '_input', - '_unhandled_input', - '_unhandled_key_input', - '_set', - '_get', # probably - 'emit_signal', # can't handle extra parameters to be sent with signal. - 'draw_mesh', # issue with one parameter, value is `Null((..), (..), (..))`` - '_to_string', # nonexistant function ._to_string - '_get_minimum_size', # Nonexistent function _get_minimum_size - ] - - - var built_ins = [] - var local_methods = [] - var _method_names = [] - - func is_blacklisted(method_meta): - return _blacklist.find(method_meta.name) != -1 - - func _add_name_if_does_not_have(method_name): - var should_add = _method_names.find(method_name) == -1 - if(should_add): - _method_names.append(method_name) - return should_add - - func add_built_in_method(method_meta): - var did_add = _add_name_if_does_not_have(method_meta.name) - if(did_add and !is_blacklisted(method_meta)): - built_ins.append(method_meta) - - func add_local_method(method_meta): - var did_add = _add_name_if_does_not_have(method_meta.name) - if(did_add): - local_methods.append(method_meta) - - func to_s(): - var text = "Locals\n" - for i in range(local_methods.size()): - text += str(" ", local_methods[i].name, "\n") - text += "Built-Ins\n" - for i in range(built_ins.size()): - text += str(" ", built_ins[i].name, "\n") - return text - -# ------------------------------------------------------------------------------ -# Helper class to deal with objects and inner classes. -# ------------------------------------------------------------------------------ -class ObjectInfo: - var _path = null - var _subpaths = [] - var _utils = load('res://addons/gut/utils.gd').get_instance() - var _lgr = _utils.get_logger() - var _method_strategy = null - var make_partial_double = false - var scene_path = null - var _native_class = null - var _native_class_name = null - var _singleton_instance = null - var _singleton_name = null - - func _init(path, subpath=null): - _path = path - if(subpath != null): - _subpaths = Array(subpath.split('/')) - - # Returns an instance of the class/inner class - func instantiate(): - var to_return = null - - if(_singleton_instance != null): - to_return = _singleton_instance - elif(is_native()): - to_return = _native_class.new() - else: - to_return = get_loaded_class().new() - - return to_return - - - # Can't call it get_class because that is reserved so it gets this ugly name. - # Loads up the class and then any inner classes to give back a reference to - # the desired Inner class (if there is any) - func get_loaded_class(): - var LoadedClass = load(_path) - for i in range(_subpaths.size()): - LoadedClass = LoadedClass.get(_subpaths[i]) - return LoadedClass - - - func to_s(): - return str(_path, '[', get_subpath(), ']') - - - func get_path(): - return _path - - - func get_subpath(): - return PoolStringArray(_subpaths).join('/') - - - func has_subpath(): - return _subpaths.size() != 0 - - - func get_method_strategy(): - return _method_strategy - - - func set_method_strategy(method_strategy): - _method_strategy = method_strategy - - - func is_native(): - return _native_class != null - - - func set_native_class(native_class): - _native_class = native_class - var inst = native_class.new() - _native_class_name = inst.get_class() - _path = _native_class_name - if(!inst is Reference): - inst.free() - - - func get_native_class_name(): - return _native_class_name - - - func get_singleton_instance(): - return _singleton_instance - - - func get_singleton_name(): - return _singleton_name - - - func set_singleton_name(singleton_name): - _singleton_name = singleton_name - _singleton_instance = _utils.get_singleton_by_name(_singleton_name) - - - func is_singleton(): - return _singleton_instance != null - - - func get_extends_text(): - var extend = null - if(is_singleton()): - extend = str("# Double of singleton ", _singleton_name, ", base class is Reference") - elif(is_native()): - var native = get_native_class_name() - if(native.begins_with('_')): - native = native.substr(1) - extend = str("extends ", native) - else: - extend = str("extends '", get_path(), "'") - - if(has_subpath()): - extend += str('.', get_subpath().replace('/', '.')) - - return extend - - - func get_constants_text(): - if(!is_singleton()): - return "" - - # do not include constants defined in the super class which for - # singletons stubs is Reference. - var exclude_constants = Array(ClassDB.class_get_integer_constant_list("Reference")) - var text = str("# -----\n# ", _singleton_name, " Constants\n# -----\n") - var constants = ClassDB.class_get_integer_constant_list(_singleton_name) - for c in constants: - if(!exclude_constants.has(c)): - var value = ClassDB.class_get_integer_constant(_singleton_name, c) - text += str("const ", c, " = ", value, "\n") - - return text - - func get_properties_text(): - if(!is_singleton()): - return "" - - var text = str("# -----\n# ", _singleton_name, " Properties\n# -----\n") - var props = ClassDB.class_get_property_list(_singleton_name) - for prop in props: - var accessors = {"setter":null, "getter":null} - var prop_text = str("var ", prop["name"]) - - var getter_name = "get_" + prop["name"] - if(ClassDB.class_has_method(_singleton_name, getter_name)): - accessors.getter = getter_name - else: - getter_name = "is_" + prop["name"] - if(ClassDB.class_has_method(_singleton_name, getter_name)): - accessors.getter = getter_name - - var setter_name = "set_" + prop["name"] - if(ClassDB.class_has_method(_singleton_name, setter_name)): - accessors.setter = setter_name - - var setget_text = "" - if(accessors.setter != null and accessors.getter != null): - setget_text = str("setget ", accessors.setter, ", ", accessors.getter) - else: - # never seen this message show up, but it should show up if we - # get misbehaving singleton. - _lgr.error(str("Could not find setget methods for property: ", - _singleton_name, ".", prop["name"])) - - text += str(prop_text, " ", setget_text, "\n") - - return text - - -# ------------------------------------------------------------------------------ -# Allows for interacting with a file but only creating a string. This was done -# to ease the transition from files being created for doubles to loading -# doubles from a string. This allows the files to be created for debugging -# purposes since reading a file is easier than reading a dumped out string. -# ------------------------------------------------------------------------------ -class FileOrString: - extends File - - var _do_file = false - var _contents = '' - var _path = null - - func open(path, mode): - _path = path - if(_do_file): - return .open(path, mode) - else: - return OK - - func close(): - if(_do_file): - return .close() - - func store_string(s): - if(_do_file): - .store_string(s) - _contents += s - - func get_contents(): - return _contents - - func get_path(): - return _path - - func load_it(): - if(_contents != ''): - var script = GDScript.new() - script.set_source_code(get_contents()) - script.reload() - return script - else: - return load(_path) - -# ------------------------------------------------------------------------------ -# A stroke of genius if I do say so. This allows for doubling a scene without -# having to write any files. By overloading the "instance" method we can -# make whatever we want. -# ------------------------------------------------------------------------------ -class PackedSceneDouble: - extends PackedScene - var _script = null - var _scene = null - - func set_script_obj(obj): - _script = obj - - func instance(edit_state=0): - var inst = _scene.instance(edit_state) - if(_script != null): - inst.set_script(_script) - return inst - - func load_scene(path): - _scene = load(path) - - - - -# ------------------------------------------------------------------------------ -# START Doubler -# ------------------------------------------------------------------------------ -var _utils = load('res://addons/gut/utils.gd').get_instance() - -var _ignored_methods = _utils.OneToMany.new() -var _stubber = _utils.Stubber.new() -var _lgr = _utils.get_logger() -var _method_maker = _utils.MethodMaker.new() - -var _output_dir = 'user://gut_temp_directory' -var _double_count = 0 # used in making files names unique -var _spy = null -var _gut = null -var _strategy = null -var _base_script_text = _utils.get_file_as_text('res://addons/gut/double_templates/script_template.txt') -var _make_files = false -# used by tests for debugging purposes. -var _print_source = false - -func _init(strategy=_utils.DOUBLE_STRATEGY.PARTIAL): - set_logger(_utils.get_logger()) - _strategy = strategy - -# ############### -# Private -# ############### -func _get_indented_line(indents, text): - var to_return = '' - for _i in range(indents): - to_return += "\t" - return str(to_return, text, "\n") - - -func _stub_to_call_super(obj_info, method_name): - if(_utils.non_super_methods.has(method_name)): - return - - var path = obj_info.get_path() - if(obj_info.is_singleton()): - path = obj_info.get_singleton_name() - elif(obj_info.scene_path != null): - path = obj_info.scene_path - - var params = _utils.StubParams.new(path, method_name, obj_info.get_subpath()) - params.to_call_super() - _stubber.add_stub(params) - - -func _get_base_script_text(obj_info, override_path, script_methods): - var path = obj_info.get_path() - if(override_path != null): - path = override_path - - var stubber_id = -1 - if(_stubber != null): - stubber_id = _stubber.get_instance_id() - - var spy_id = -1 - if(_spy != null): - spy_id = _spy.get_instance_id() - - var gut_id = -1 - if(_gut != null): - gut_id = _gut.get_instance_id() - - var values = { - # Top sections - "extends":obj_info.get_extends_text(), - "constants":obj_info.get_constants_text(), - "properties":obj_info.get_properties_text(), - - # metadata values - "path":path, - "subpath":obj_info.get_subpath(), - "stubber_id":stubber_id, - "spy_id":spy_id, - "gut_id":gut_id, - "singleton_name":_utils.nvl(obj_info.get_singleton_name(), ''), - "is_partial":str(obj_info.make_partial_double).to_lower() - } - - return _base_script_text.format(values) - - -func _write_file(obj_info, dest_path, override_path=null): - var script_methods = _get_methods(obj_info) - var base_script = _get_base_script_text(obj_info, override_path, script_methods) - var super_name = "" - var path = "" - - if(obj_info.is_singleton()): - super_name = obj_info.get_singleton_name() - else: - path = obj_info.get_path() - - var f = FileOrString.new() - f._do_file = _make_files - var f_result = f.open(dest_path, f.WRITE) - - if(f_result != OK): - _lgr.error(str('Error creating file ', dest_path)) - _lgr.error(str('Could not create double for :', obj_info.to_s())) - return - - f.store_string(base_script) - - for i in range(script_methods.local_methods.size()): - f.store_string(_get_func_text(script_methods.local_methods[i], path, super_name)) - - for i in range(script_methods.built_ins.size()): - _stub_to_call_super(obj_info, script_methods.built_ins[i].name) - f.store_string(_get_func_text(script_methods.built_ins[i], path, super_name)) - - f.close() - if(_print_source): - print(f.get_contents()) - return f - - -func _double_scene_and_script(scene_info): - var to_return = PackedSceneDouble.new() - to_return.load_scene(scene_info.get_path()) - - var inst = load(scene_info.get_path()).instance() - var script_path = null - if(inst.get_script()): - script_path = inst.get_script().get_path() - inst.free() - - if(script_path): - var oi = ObjectInfo.new(script_path) - oi.set_method_strategy(scene_info.get_method_strategy()) - oi.make_partial_double = scene_info.make_partial_double - oi.scene_path = scene_info.get_path() - to_return.set_script_obj(_double(oi, scene_info.get_path()).load_it()) - - return to_return - - -func _get_methods(object_info): - var obj = object_info.instantiate() - # any method in the script or super script - var script_methods = ScriptMethods.new() - var methods = obj.get_method_list() - - if(!object_info.is_singleton() and !(obj is Reference)): - obj.free() - - # first pass is for local methods only - for i in range(methods.size()): - if(object_info.is_singleton()): - #print(methods[i].name, " :: ", methods[i].flags, " :: ", methods[i].id) - #print(" ", methods[i]) - - # It appears that the ID for methods upstream from a singleton are - # below 200. Initially it was thought that singleton specific methods - # were above 1000. This was true for Input but not for OS. I've - # changed the condition to be > 200 instead of > 1000. It will take - # some investigation to figure out if this is right, but it works - # for now. Someone either find an issue and open a bug, or this will - # just exist like this. Sorry future me (or someone else). - if(methods[i].id > 200 and methods[i].flags in [1, 9]): - script_methods.add_local_method(methods[i]) - - # 65 is a magic number for methods in script, though documentation - # says 64. This picks up local overloads of base class methods too. - # See MethodFlags in @GlobalScope - elif(methods[i].flags == 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])): - script_methods.add_local_method(methods[i]) - - if(object_info.get_method_strategy() == _utils.DOUBLE_STRATEGY.FULL): - # second pass is for anything not local - for j in range(methods.size()): - # 65 is a magic number for methods in script, though documentation - # says 64. This picks up local overloads of base class methods too. - if(methods[j].flags != 65 and !_ignored_methods.has(object_info.get_path(), methods[j]['name'])): - script_methods.add_built_in_method(methods[j]) - - return script_methods - - -func _get_inst_id_ref_str(inst): - var ref_str = 'null' - if(inst): - ref_str = str('instance_from_id(', inst.get_instance_id(),')') - return ref_str - - -func _get_func_text(method_hash, path, super=""): - var override_count = null; - if(_stubber != null): - override_count = _stubber.get_parameter_count(path, method_hash.name) - - var text = _method_maker.get_function_text(method_hash, path, override_count, super) + "\n" - - return text - -# returns the path to write the double file to -func _get_temp_path(object_info): - var file_name = null - var extension = null - - if(object_info.is_singleton()): - file_name = str(object_info.get_singleton_instance()) - extension = "gd" - elif(object_info.is_native()): - file_name = object_info.get_native_class_name() - extension = 'gd' - else: - file_name = object_info.get_path().get_file().get_basename() - extension = object_info.get_path().get_extension() - - if(object_info.has_subpath()): - file_name += '__' + object_info.get_subpath().replace('/', '__') - - file_name += str('__dbl', _double_count, '__.', extension) - - var to_return = _output_dir.plus_file(file_name) - return to_return - - -func _double(obj_info, override_path=null): - var temp_path = _get_temp_path(obj_info) - var result = _write_file(obj_info, temp_path, override_path) - _double_count += 1 - return result - - -func _double_script(path, make_partial, strategy): - var oi = ObjectInfo.new(path) - oi.make_partial_double = make_partial - oi.set_method_strategy(strategy) - return _double(oi).load_it() - - -func _double_inner(path, subpath, make_partial, strategy): - var oi = ObjectInfo.new(path, subpath) - oi.set_method_strategy(strategy) - oi.make_partial_double = make_partial - return _double(oi).load_it() - - -func _double_scene(path, make_partial, strategy): - var oi = ObjectInfo.new(path) - oi.set_method_strategy(strategy) - oi.make_partial_double = make_partial - return _double_scene_and_script(oi) - - -func _double_gdnative(native_class, make_partial, strategy): - var oi = ObjectInfo.new(null) - oi.set_native_class(native_class) - oi.set_method_strategy(strategy) - oi.make_partial_double = make_partial - return _double(oi).load_it() - - -func _double_singleton(singleton_name, make_partial, strategy): - var oi = ObjectInfo.new(null) - oi.set_singleton_name(singleton_name) - oi.set_method_strategy(_utils.DOUBLE_STRATEGY.PARTIAL) - oi.make_partial_double = make_partial - return _double(oi).load_it() - -# ############### -# Public -# ############### -func get_output_dir(): - return _output_dir - - -func set_output_dir(output_dir): - if(output_dir != null): - _output_dir = output_dir - if(_make_files): - var d = Directory.new() - d.make_dir_recursive(output_dir) - - -func get_spy(): - return _spy - - -func set_spy(spy): - _spy = spy - - -func get_stubber(): - return _stubber - - -func set_stubber(stubber): - _stubber = stubber - - -func get_logger(): - return _lgr - - -func set_logger(logger): - _lgr = logger - _method_maker.set_logger(logger) - - -func get_strategy(): - return _strategy - - -func set_strategy(strategy): - _strategy = strategy - - -func get_gut(): - return _gut - - -func set_gut(gut): - _gut = gut - - -func partial_double_scene(path, strategy=_strategy): - return _double_scene(path, true, strategy) - - -# double a scene -func double_scene(path, strategy=_strategy): - return _double_scene(path, false, strategy) - - -# double a script/object -func double(path, strategy=_strategy): - return _double_script(path, false, strategy) - - -func partial_double(path, strategy=_strategy): - return _double_script(path, true, strategy) - - -func partial_double_inner(path, subpath, strategy=_strategy): - return _double_inner(path, subpath, true, strategy) - - -# double an inner class in a script -func double_inner(path, subpath, strategy=_strategy): - return _double_inner(path, subpath, false, strategy) - - -# must always use FULL strategy since this is a native class and you won't get -# any methods if you don't use FULL -func double_gdnative(native_class): - return _double_gdnative(native_class, false, _utils.DOUBLE_STRATEGY.FULL) - - -# must always use FULL strategy since this is a native class and you won't get -# any methods if you don't use FULL -func partial_double_gdnative(native_class): - return _double_gdnative(native_class, true, _utils.DOUBLE_STRATEGY.FULL) - - -func double_singleton(name): - return _double_singleton(name, false, _utils.DOUBLE_STRATEGY.PARTIAL) - - -func partial_double_singleton(name): - return _double_singleton(name, true, _utils.DOUBLE_STRATEGY.PARTIAL) - - -func clear_output_directory(): - if(!_make_files): - return false - - var did = false - if(_output_dir.find('user://') == 0): - var d = Directory.new() - var result = d.open(_output_dir) - # BIG GOTCHA HERE. If it cannot open the dir w/ erro 31, then the - # directory becomes res:// and things go on normally and gut clears out - # out res:// which is SUPER BAD. - if(result == OK): - d.list_dir_begin(true) - var f = d.get_next() - while(f != ''): - d.remove(f) - f = d.get_next() - did = true - return did - -func delete_output_directory(): - var did = clear_output_directory() - if(did): - var d = Directory.new() - d.remove(_output_dir) - - -func add_ignored_method(path, method_name): - _ignored_methods.add(path, method_name) - - -func get_ignored_methods(): - return _ignored_methods - - -func get_make_files(): - return _make_files - - -func set_make_files(make_files): - _make_files = make_files - set_output_dir(_output_dir) - -func get_method_maker(): - return _method_maker |
