# ------------- # returns{} and parameters {} have the followin structure # ------------- # { # inst_id_or_path1:{ # method_name1: [StubParams, StubParams], # method_name2: [StubParams, StubParams] # }, # inst_id_or_path2:{ # method_name1: [StubParams, StubParams], # method_name2: [StubParams, StubParams] # } # } var returns = {} var _utils = load("res://addons/gut/utils.gd").get_instance() var _lgr = _utils.get_logger() var _strutils = _utils.Strutils.new() var _class_db_name_hash = {} func _init(): _class_db_name_hash = _make_crazy_dynamic_over_engineered_class_db_hash() # So, I couldn't figure out how to get to a reference for a GDNative Class # using a string. ClassDB has all thier names...so I made a hash using those # names and the classes. Then I dynmaically make a script that has that as # the source and grab the hash out of it and return it. Super Rube Golbergery, # but tons of fun. func _make_crazy_dynamic_over_engineered_class_db_hash(): var text = "var all_the_classes = {\n" for classname in ClassDB.get_class_list(): if ClassDB.can_instantiate(classname): text += str('"', classname, '": ', classname, ", \n") else: text += str("# ", classname, "\n") text += "}" var inst = _utils.create_script_from_source(text).new() return inst.all_the_classes func _find_matches(obj, method): var matches = null var last_not_null_parent = null # Search for what is passed in first. This could be a class or an instance. # We want to find the instance before we find the class. If we do not have # an entry for the instance then see if we have an entry for the class. if returns.has(obj) and returns[obj].has(method): matches = returns[obj][method] elif _utils.is_instance(obj): var parent = obj.get_script() var found = false while parent != null and !found: found = returns.has(parent) if !found: last_not_null_parent = parent parent = parent.get_base_script() # Could not find the script so check to see if a native class of this # type was stubbed. if !found: var base_type = last_not_null_parent.get_instance_base_type() if _class_db_name_hash.has(base_type): parent = _class_db_name_hash[base_type] found = returns.has(parent) if found and returns[parent].has(method): matches = returns[parent][method] return matches # Searches returns for an entry that matches the instance or the class that # passed in obj is. # # obj can be an instance, class, or a path. func _find_stub(obj, method, parameters = null, find_overloads = false): var to_return = null var matches = _find_matches(obj, method) if matches == null: return null var param_match = null var null_match = null var overload_match = null for i in range(matches.size()): var cur_stub = matches[i] if cur_stub.parameters == parameters: param_match = cur_stub if cur_stub.parameters == null and !cur_stub.is_param_override_only(): null_match = cur_stub if cur_stub.has_param_override(): if overload_match == null || overload_match.is_script_default: overload_match = cur_stub if find_overloads and overload_match != null: to_return = overload_match # We have matching parameter values so return the stub value for that elif param_match != null: to_return = param_match # We found a case where the parameters were not specified so return # parameters for that. Only do this if the null match is not *just* # a paramerter override stub. elif null_match != null: to_return = null_match return to_return # ############## # Public # ############## func add_stub(stub_params): stub_params._lgr = _lgr var key = stub_params.stub_target if !returns.has(key): returns[key] = {} if !returns[key].has(stub_params.stub_method): returns[key][stub_params.stub_method] = [] returns[key][stub_params.stub_method].append(stub_params) # Gets a stubbed return value for the object and method passed in. If the # instance was stubbed it will use that, otherwise it will use the path and # subpath of the object to try to find a value. # # It will also use the optional list of parameter values to find a value. If # the object was stubbed with no parameters than any parameters will match. # If it was stubbed with specific parameter values then it will try to match. # If the parameters do not match BUT there was also an empty parameter list stub # then it will return those. # If it cannot find anything that matches then null is returned.for # # Parameters # obj: this should be an instance of a doubled object. # method: the method called # parameters: optional array of parameter vales to find a return value for. func get_return(obj, method, parameters = null): var stub_info = _find_stub(obj, method, parameters) if stub_info != null: return stub_info.return_val else: _lgr.info( str( "Call to [", method, "] was not stubbed for the supplied parameters ", parameters, ". Null was returned." ) ) return null func should_call_super(obj, method, parameters = null): var stub_info = _find_stub(obj, method, parameters) var is_partial = false if typeof(obj) != TYPE_STRING: # some stubber tests test with strings is_partial = obj.__gutdbl.is_partial var should = is_partial if stub_info != null: should = stub_info.call_super elif !is_partial: # this log message is here because of how the generated doubled scripts # are structured. With this log msg here, you will only see one # "unstubbed" info instead of multiple. _lgr.info("Unstubbed call to " + method + "::" + _strutils.type2str(obj)) should = false return should func get_parameter_count(obj, method): var to_return = null var stub_info = _find_stub(obj, method, null, true) if stub_info != null and stub_info.has_param_override(): to_return = stub_info.parameter_count return to_return func get_default_value(obj, method, p_index): var to_return = null var stub_info = _find_stub(obj, method, null, true) if ( stub_info != null and stub_info.parameter_defaults != null and stub_info.parameter_defaults.size() > p_index ): to_return = stub_info.parameter_defaults[p_index] return to_return func clear(): returns.clear() func get_logger(): return _lgr func set_logger(logger): _lgr = logger func to_s(): var text = "" for thing in returns: text += str("-- ", thing, " --\n") for method in returns[thing]: text += str("\t", method, "\n") for i in range(returns[thing][method].size()): text += "\t\t" + returns[thing][method][i].to_s() + "\n" if text == "": text = "Stubber is empty" return text func stub_defaults_from_meta(target, method_meta): var params = _utils.StubParams.new(target, method_meta) params.is_script_default = true add_stub(params)