summaryrefslogtreecommitdiff
path: root/addons/gut/utils.gd
diff options
context:
space:
mode:
authorSophia Pearson <codergal89@gmail.com>2022-05-20 00:45:25 +0200
committerSophia Pearson <codergal89@gmail.com>2022-05-20 18:56:04 +0200
commit05d29ccce1898ed89c0b650c77242c2fa2805128 (patch)
treee8ee3bcb570fa6f3d9d96273c2bf4d4c8618d08b /addons/gut/utils.gd
downloadtexty-05d29ccce1898ed89c0b650c77242c2fa2805128.tar.xz
texty-05d29ccce1898ed89c0b650c77242c2fa2805128.zip
texty: initial commit
Diffstat (limited to 'addons/gut/utils.gd')
-rw-r--r--addons/gut/utils.gd389
1 files changed, 389 insertions, 0 deletions
diff --git a/addons/gut/utils.gd b/addons/gut/utils.gd
new file mode 100644
index 0000000..50e8950
--- /dev/null
+++ b/addons/gut/utils.gd
@@ -0,0 +1,389 @@
+# ##############################################################################
+#(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
+# -----------
+# This class is a PSUEDO SINGLETON. You should not make instances of it but use
+# the get_instance static method.
+# ##############################################################################
+extends Node
+
+# ------------------------------------------------------------------------------
+# The instance name as a function since you can't have static variables.
+# ------------------------------------------------------------------------------
+static func INSTANCE_NAME():
+ return '__GutUtilsInstName__'
+
+# ------------------------------------------------------------------------------
+# Gets the root node without having to be in the tree and pushing out an error
+# if we don't have a main loop ready to go yet.
+# ------------------------------------------------------------------------------
+static func get_root_node():
+ var to_return = null
+ var main_loop = Engine.get_main_loop()
+ if(main_loop != null):
+ return main_loop.root
+ else:
+ push_error('No Main Loop Yet')
+ return null
+
+# ------------------------------------------------------------------------------
+# Get the ONE instance of utils
+# ------------------------------------------------------------------------------
+static func get_instance():
+ var the_root = get_root_node()
+ var inst = null
+ if(the_root.has_node(INSTANCE_NAME())):
+ inst = the_root.get_node(INSTANCE_NAME())
+ else:
+ inst = load('res://addons/gut/utils.gd').new()
+ inst.set_name(INSTANCE_NAME())
+ the_root.add_child(inst)
+ return inst
+
+var Logger = load('res://addons/gut/logger.gd') # everything should use get_logger
+var _lgr = null
+
+var _test_mode = false
+
+var AutoFree = load('res://addons/gut/autofree.gd')
+var Comparator = load('res://addons/gut/comparator.gd')
+var CompareResult = load('res://addons/gut/compare_result.gd')
+var DiffTool = load('res://addons/gut/diff_tool.gd')
+var Doubler = load('res://addons/gut/doubler.gd')
+var Gut = load('res://addons/gut/gut.gd')
+var HookScript = load('res://addons/gut/hook_script.gd')
+var InputFactory = load("res://addons/gut/input_factory.gd")
+var InputSender = load("res://addons/gut/input_sender.gd")
+var JunitXmlExport = load('res://addons/gut/junit_xml_export.gd')
+var MethodMaker = load('res://addons/gut/method_maker.gd')
+var OneToMany = load('res://addons/gut/one_to_many.gd')
+var OrphanCounter = load('res://addons/gut/orphan_counter.gd')
+var ParameterFactory = load('res://addons/gut/parameter_factory.gd')
+var ParameterHandler = load('res://addons/gut/parameter_handler.gd')
+var Printers = load('res://addons/gut/printers.gd')
+var ResultExporter = load('res://addons/gut/result_exporter.gd')
+var Spy = load('res://addons/gut/spy.gd')
+var Strutils = load('res://addons/gut/strutils.gd')
+var Stubber = load('res://addons/gut/stubber.gd')
+var StubParams = load('res://addons/gut/stub_params.gd')
+var Summary = load('res://addons/gut/summary.gd')
+var Test = load('res://addons/gut/test.gd')
+var TestCollector = load('res://addons/gut/test_collector.gd')
+var ThingCounter = load('res://addons/gut/thing_counter.gd')
+
+# Source of truth for the GUT version
+var version = '7.3.0'
+# The required Godot version as an array.
+var req_godot = [3, 2, 0]
+# Used for doing file manipulation stuff so as to not keep making File instances.
+# could be a bit of overkill but who cares.
+var _file_checker = File.new()
+# Online fetch of the latest version available on github
+var latest_version = null
+var should_display_latest_version = false
+
+
+# These methods all call super implicitly. Stubbing them to call super causes
+# super to be called twice.
+var non_super_methods = [
+ "_init",
+ "_ready",
+ "_notification",
+ "_enter_world",
+ "_exit_world",
+ "_process",
+ "_physics_process",
+ "_exit_tree",
+ "_gui_input ",
+]
+
+
+func _ready() -> void:
+ _http_request_latest_version()
+
+func _http_request_latest_version() -> void:
+ var http_request = HTTPRequest.new()
+ http_request.name = "http_request"
+ add_child(http_request)
+ http_request.connect("request_completed", self, "_on_http_request_latest_version_completed")
+ # Perform a GET request. The URL below returns JSON as of writing.
+ var error = http_request.request("https://api.github.com/repos/bitwes/Gut/releases/latest")
+
+func _on_http_request_latest_version_completed(result, response_code, headers, body):
+ if not result == HTTPRequest.RESULT_SUCCESS:
+ return
+
+ var response = parse_json(body.get_string_from_utf8())
+ # Will print the user agent string used by the HTTPRequest node (as recognized by httpbin.org).
+ if response:
+ if response.get("html_url"):
+ latest_version = Array(response.html_url.split("/")).pop_back().right(1)
+ if latest_version != version:
+ should_display_latest_version = true
+
+
+
+const GUT_METADATA = '__gut_metadata_'
+
+enum DOUBLE_STRATEGY{
+ FULL,
+ PARTIAL
+}
+
+enum DIFF {
+ DEEP,
+ SHALLOW,
+ SIMPLE
+}
+
+# ------------------------------------------------------------------------------
+# Blurb of text with GUT and Godot versions.
+# ------------------------------------------------------------------------------
+func get_version_text():
+ var v_info = Engine.get_version_info()
+ var gut_version_info = str('GUT version: ', version)
+ var godot_version_info = str('Godot version: ', v_info.major, '.', v_info.minor, '.', v_info.patch)
+ return godot_version_info + "\n" + gut_version_info
+
+
+# ------------------------------------------------------------------------------
+# Returns a nice string for erroring out when we have a bad Godot version.
+# ------------------------------------------------------------------------------
+func get_bad_version_text():
+ var ver = PoolStringArray(req_godot).join('.')
+ var info = Engine.get_version_info()
+ var gd_version = str(info.major, '.', info.minor, '.', info.patch)
+ return 'GUT ' + version + ' requires Godot ' + ver + ' or greater. Godot version is ' + gd_version
+
+
+# ------------------------------------------------------------------------------
+# Checks the Godot version against req_godot array.
+# ------------------------------------------------------------------------------
+func is_version_ok(engine_info=Engine.get_version_info(),required=req_godot):
+ var is_ok = null
+ var engine_array = [engine_info.major, engine_info.minor, engine_info.patch]
+
+ var idx = 0
+ while(is_ok == null and idx < engine_array.size()):
+ if(int(engine_array[idx]) > int(required[idx])):
+ is_ok = true
+ elif(int(engine_array[idx]) < int(required[idx])):
+ is_ok = false
+
+ idx += 1
+
+ # still null means each index was the same.
+ return nvl(is_ok, true)
+
+
+# ------------------------------------------------------------------------------
+# Everything should get a logger through this.
+#
+# When running in test mode this will always return a new logger so that errors
+# are not caused by getting bad warn/error/etc counts.
+# ------------------------------------------------------------------------------
+func get_logger():
+ if(_test_mode):
+ return Logger.new()
+ else:
+ if(_lgr == null):
+ _lgr = Logger.new()
+ return _lgr
+
+
+
+# ------------------------------------------------------------------------------
+# return if_null if value is null otherwise return value
+# ------------------------------------------------------------------------------
+func nvl(value, if_null):
+ if(value == null):
+ return if_null
+ else:
+ return value
+
+
+# ------------------------------------------------------------------------------
+# returns true if the object has been freed, false if not
+#
+# From what i've read, the weakref approach should work. It seems to work most
+# of the time but sometimes it does not catch it. The str comparison seems to
+# fill in the gaps. I've not seen any errors after adding that check.
+# ------------------------------------------------------------------------------
+func is_freed(obj):
+ var wr = weakref(obj)
+ return !(wr.get_ref() and str(obj) != '[Deleted Object]')
+
+
+# ------------------------------------------------------------------------------
+# Pretty self explanitory.
+# ------------------------------------------------------------------------------
+func is_not_freed(obj):
+ return !is_freed(obj)
+
+
+# ------------------------------------------------------------------------------
+# Checks if the passed in object is a GUT Double or Partial Double.
+# ------------------------------------------------------------------------------
+func is_double(obj):
+ var to_return = false
+ if(typeof(obj) == TYPE_OBJECT and is_instance_valid(obj)):
+ to_return = obj.has_method('__gut_instance_from_id')
+ return to_return
+
+
+# ------------------------------------------------------------------------------
+# Checks if the passed in is an instance of a class
+# ------------------------------------------------------------------------------
+func is_instance(obj):
+ return typeof(obj) == TYPE_OBJECT and !obj.has_method('new') and !obj.has_method('instance')
+
+# ------------------------------------------------------------------------------
+# Checks if the passed in is a GDScript
+# ------------------------------------------------------------------------------
+func is_gdscript(obj):
+ return typeof(obj) == TYPE_OBJECT and str(obj).begins_with('[GDScript:')
+
+# ------------------------------------------------------------------------------
+# Returns an array of values by calling get(property) on each element in source
+# ------------------------------------------------------------------------------
+func extract_property_from_array(source, property):
+ var to_return = []
+ for i in (source.size()):
+ to_return.append(source[i].get(property))
+ return to_return
+
+
+# ------------------------------------------------------------------------------
+# true if file exists, false if not.
+# ------------------------------------------------------------------------------
+func file_exists(path):
+ return _file_checker.file_exists(path)
+
+
+# ------------------------------------------------------------------------------
+# Write a file.
+# ------------------------------------------------------------------------------
+func write_file(path, content):
+ var f = File.new()
+ var result = f.open(path, f.WRITE)
+ if(result == OK):
+ f.store_string(content)
+ f.close()
+
+ return result
+
+# ------------------------------------------------------------------------------
+# true if what is passed in is null or an empty string.
+# ------------------------------------------------------------------------------
+func is_null_or_empty(text):
+ return text == null or text == ''
+
+
+# ------------------------------------------------------------------------------
+# Get the name of a native class or null if the object passed in is not a
+# native class.
+# ------------------------------------------------------------------------------
+func get_native_class_name(thing):
+ var to_return = null
+ if(is_native_class(thing)):
+ var newone = thing.new()
+ to_return = newone.get_class()
+ if(!newone is Reference):
+ newone.free()
+ return to_return
+
+
+# ------------------------------------------------------------------------------
+# Checks an object to see if it is a GDScriptNativeClass
+# ------------------------------------------------------------------------------
+func is_native_class(thing):
+ var it_is = false
+ if(typeof(thing) == TYPE_OBJECT):
+ it_is = str(thing).begins_with("[GDScriptNativeClass:")
+ return it_is
+
+
+# ------------------------------------------------------------------------------
+# Returns the text of a file or an empty string if the file could not be opened.
+# ------------------------------------------------------------------------------
+func get_file_as_text(path):
+ var to_return = ''
+ var f = File.new()
+ var result = f.open(path, f.READ)
+ if(result == OK):
+ to_return = f.get_as_text()
+ f.close()
+ return to_return
+
+
+# ------------------------------------------------------------------------------
+# Loops through an array of things and calls a method or checks a property on
+# each element until it finds the returned value. The item in the array is
+# returned or null if it is not found.
+# ------------------------------------------------------------------------------
+func search_array(ar, prop_method, value):
+ var found = false
+ var idx = 0
+
+ while(idx < ar.size() and !found):
+ var item = ar[idx]
+ if(item.get(prop_method) != null):
+ if(item.get(prop_method) == value):
+ found = true
+ elif(item.has_method(prop_method)):
+ if(item.call(prop_method) == value):
+ found = true
+
+ if(!found):
+ idx += 1
+
+ if(found):
+ return ar[idx]
+ else:
+ return null
+
+
+func are_datatypes_same(got, expected):
+ return !(typeof(got) != typeof(expected) and got != null and expected != null)
+
+
+func pretty_print(dict):
+ print(str(JSON.print(dict, ' ')))
+
+
+func get_script_text(obj):
+ return obj.get_script().get_source_code()
+
+
+func get_singleton_by_name(name):
+ var source = str("var singleton = ", name)
+ var script = GDScript.new()
+ script.set_source_code(source)
+ script.reload()
+ return script.new().singleton