summaryrefslogtreecommitdiff
path: root/addons/gut/test_collector.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/test_collector.gd
downloadtexty-05d29ccce1898ed89c0b650c77242c2fa2805128.tar.xz
texty-05d29ccce1898ed89c0b650c77242c2fa2805128.zip
texty: initial commit
Diffstat (limited to 'addons/gut/test_collector.gd')
-rw-r--r--addons/gut/test_collector.gd286
1 files changed, 286 insertions, 0 deletions
diff --git a/addons/gut/test_collector.gd b/addons/gut/test_collector.gd
new file mode 100644
index 0000000..c8f5d8b
--- /dev/null
+++ b/addons/gut/test_collector.gd
@@ -0,0 +1,286 @@
+# ------------------------------------------------------------------------------
+# Used to keep track of info about each test ran.
+# ------------------------------------------------------------------------------
+class Test:
+ # indicator if it passed or not. defaults to true since it takes only
+ # one failure to make it not pass. _fail in gut will set this.
+ var passed = true
+ # the name of the function
+ var name = ""
+ # flag to know if the name has been printed yet.
+ var has_printed_name = false
+ # the number of arguments the method has
+ var arg_count = 0
+ # The number of asserts in the test
+ var assert_count = 0
+ # if the test has been marked pending at anypont during
+ # execution.
+ var pending = false
+
+
+# ------------------------------------------------------------------------------
+# This holds all the meta information for a test script. It contains the
+# name of the inner class and an array of Test "structs".
+#
+# This class also facilitates all the exporting and importing of tests.
+# ------------------------------------------------------------------------------
+class TestScript:
+ var inner_class_name = null
+ var tests = []
+ var path = null
+ var _utils = null
+ var _lgr = null
+
+ func _init(utils=null, logger=null):
+ _utils = utils
+ _lgr = logger
+
+ func to_s():
+ var to_return = path
+ if(inner_class_name != null):
+ to_return += str('.', inner_class_name)
+ to_return += "\n"
+ for i in range(tests.size()):
+ to_return += str(' ', tests[i].name, "\n")
+ return to_return
+
+ func get_new():
+ return load_script().new()
+
+ func load_script():
+ #print('loading: ', get_full_name())
+ var to_return = load(path)
+ if(inner_class_name != null):
+ # If we wanted to do inner classes in inner classses
+ # then this would have to become some kind of loop or recursive
+ # call to go all the way down the chain or this class would
+ # have to change to hold onto the loaded class instead of
+ # just path information.
+ to_return = to_return.get(inner_class_name)
+ return to_return
+
+ func get_filename_and_inner():
+ var to_return = get_filename()
+ if(inner_class_name != null):
+ to_return += '.' + inner_class_name
+ return to_return
+
+ func get_full_name():
+ var to_return = path
+ if(inner_class_name != null):
+ to_return += '.' + inner_class_name
+ return to_return
+
+ func get_filename():
+ return path.get_file()
+
+ func has_inner_class():
+ return inner_class_name != null
+
+ # Note: although this no longer needs to export the inner_class names since
+ # they are pulled from metadata now, it is easier to leave that in
+ # so we don't have to cut the export down to unique script names.
+ func export_to(config_file, section):
+ config_file.set_value(section, 'path', path)
+ config_file.set_value(section, 'inner_class', inner_class_name)
+ var names = []
+ for i in range(tests.size()):
+ names.append(tests[i].name)
+ config_file.set_value(section, 'tests', names)
+
+ func _remap_path(source_path):
+ var to_return = source_path
+ if(!_utils.file_exists(source_path)):
+ _lgr.debug('Checking for remap for: ' + source_path)
+ var remap_path = source_path.get_basename() + '.gd.remap'
+ if(_utils.file_exists(remap_path)):
+ var cf = ConfigFile.new()
+ cf.load(remap_path)
+ to_return = cf.get_value('remap', 'path')
+ else:
+ _lgr.warn('Could not find remap file ' + remap_path)
+ return to_return
+
+ func import_from(config_file, section):
+ path = config_file.get_value(section, 'path')
+ path = _remap_path(path)
+ # Null is an acceptable value, but you can't pass null as a default to
+ # get_value since it thinks you didn't send a default...then it spits
+ # out red text. This works around that.
+ var inner_name = config_file.get_value(section, 'inner_class', 'Placeholder')
+ if(inner_name != 'Placeholder'):
+ inner_class_name = inner_name
+ else: # just being explicit
+ inner_class_name = null
+
+ func get_test_named(name):
+ return _utils.search_array(tests, 'name', name)
+
+# ------------------------------------------------------------------------------
+# start test_collector, I don't think I like the name.
+# ------------------------------------------------------------------------------
+var scripts = []
+var _test_prefix = 'test_'
+var _test_class_prefix = 'Test'
+
+var _utils = load('res://addons/gut/utils.gd').get_instance()
+var _lgr = _utils.get_logger()
+
+func _does_inherit_from_test(thing):
+ var base_script = thing.get_base_script()
+ var to_return = false
+ if(base_script != null):
+ var base_path = base_script.get_path()
+ if(base_path == 'res://addons/gut/test.gd'):
+ to_return = true
+ else:
+ to_return = _does_inherit_from_test(base_script)
+ return to_return
+
+func _populate_tests(test_script):
+ var methods = test_script.load_script().get_script_method_list()
+ for i in range(methods.size()):
+ var name = methods[i]['name']
+ if(name.begins_with(_test_prefix)):
+ var t = Test.new()
+ t.name = name
+ t.arg_count = methods[i]['args'].size()
+ test_script.tests.append(t)
+
+func _get_inner_test_class_names(loaded):
+ var inner_classes = []
+ var const_map = loaded.get_script_constant_map()
+ for key in const_map:
+ var thing = const_map[key]
+ if(_utils.is_gdscript(thing)):
+ if(key.begins_with(_test_class_prefix)):
+ if(_does_inherit_from_test(thing)):
+ inner_classes.append(key)
+ else:
+ _lgr.warn(str('Ignoring Inner Class ', key,
+ ' because it does not extend res://addons/gut/test.gd'))
+
+ # This could go deeper and find inner classes within inner classes
+ # but requires more experimentation. Right now I'm keeping it at
+ # one level since that is what the previous version did and there
+ # has been no demand for deeper nesting.
+ # _populate_inner_test_classes(thing)
+ return inner_classes
+
+func _parse_script(test_script):
+ var inner_classes = []
+ var scripts_found = []
+
+ var loaded = load(test_script.path)
+ if(_does_inherit_from_test(loaded)):
+ _populate_tests(test_script)
+ scripts_found.append(test_script.path)
+ inner_classes = _get_inner_test_class_names(loaded)
+
+ for i in range(inner_classes.size()):
+ var loaded_inner = loaded.get(inner_classes[i])
+ if(_does_inherit_from_test(loaded_inner)):
+ var ts = TestScript.new(_utils, _lgr)
+ ts.path = test_script.path
+ ts.inner_class_name = inner_classes[i]
+ _populate_tests(ts)
+ scripts.append(ts)
+ scripts_found.append(test_script.path + '[' + inner_classes[i] +']')
+
+ return scripts_found
+
+# -----------------
+# Public
+# -----------------
+func add_script(path):
+ # SHORTCIRCUIT
+ if(has_script(path)):
+ return []
+
+ var f = File.new()
+ # SHORTCIRCUIT
+ if(!f.file_exists(path)):
+ _lgr.error('Could not find script: ' + path)
+ return
+
+ var ts = TestScript.new(_utils, _lgr)
+ ts.path = path
+ scripts.append(ts)
+ return _parse_script(ts)
+
+func clear():
+ scripts.clear()
+
+func has_script(path):
+ var found = false
+ var idx = 0
+ while(idx < scripts.size() and !found):
+ if(scripts[idx].get_full_name() == path):
+ found = true
+ else:
+ idx += 1
+ return found
+
+func export_tests(path):
+ var success = true
+ var f = ConfigFile.new()
+ for i in range(scripts.size()):
+ scripts[i].export_to(f, str('TestScript-', i))
+ var result = f.save(path)
+ if(result != OK):
+ _lgr.error(str('Could not save exported tests to [', path, ']. Error code: ', result))
+ success = false
+ return success
+
+func import_tests(path):
+ var success = false
+ var f = ConfigFile.new()
+ var result = f.load(path)
+ if(result != OK):
+ _lgr.error(str('Could not load exported tests from [', path, ']. Error code: ', result))
+ else:
+ var sections = f.get_sections()
+ for key in sections:
+ var ts = TestScript.new(_utils, _lgr)
+ ts.import_from(f, key)
+ _populate_tests(ts)
+ scripts.append(ts)
+ success = true
+ return success
+
+func get_script_named(name):
+ return _utils.search_array(scripts, 'get_filename_and_inner', name)
+
+func get_test_named(script_name, test_name):
+ var s = get_script_named(script_name)
+ if(s != null):
+ return s.get_test_named(test_name)
+ else:
+ return null
+
+func to_s():
+ var to_return = ''
+ for i in range(scripts.size()):
+ to_return += scripts[i].to_s() + "\n"
+ return to_return
+
+# ---------------------
+# Accessors
+# ---------------------
+func get_logger():
+ return _lgr
+
+func set_logger(logger):
+ _lgr = logger
+
+func get_test_prefix():
+ return _test_prefix
+
+func set_test_prefix(test_prefix):
+ _test_prefix = test_prefix
+
+func get_test_class_prefix():
+ return _test_class_prefix
+
+func set_test_class_prefix(test_class_prefix):
+ _test_class_prefix = test_class_prefix