class_name GutTest # ############################################################################## #(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. # # ############################################################################## # View readme for usage details. # # Version - see gut.gd # ############################################################################## # Class that all test scripts must extend. # # This provides all the asserts and other testing features. Test scripts are # run by the Gut class in gut.gd # ############################################################################## extends Node # ------------------------------------------------------------------------------ # Helper class to hold info for objects to double. This extracts info and has # some convenience methods. This is key in being able to make the "smart double" # method which makes doubling much easier for the user. # ----------------------------------------------------------------------------- class DoubleInfo: var path var subpath var strategy var make_partial var extension var _utils = load("res://addons/gut/utils.gd").get_instance() var _is_native = false var is_valid = false # Flexible init method. p2 can be subpath or stategy unless p3 is # specified, then p2 must be subpath and p3 is strategy. # # Examples: # (object_to_double) # (object_to_double, subpath) # (object_to_double, strategy) # (object_to_double, subpath, strategy) func _init(thing, p2 = null, p3 = null): strategy = p2 # short-circuit and ensure that is_valid # is not set to true. if _utils.is_instance(thing): return if typeof(p2) == TYPE_STRING: strategy = p3 subpath = p2 if typeof(thing) == TYPE_OBJECT: if _utils.is_native_class(thing): path = thing _is_native = true extension = "native_class_not_used" else: path = thing.resource_path else: path = thing if !_is_native: extension = path.get_extension() is_valid = true func is_scene(): return extension == "tscn" func is_script(): return extension == "gd" func is_native(): return _is_native # ------------------------------------------------------------------------------ # Begin test.gd # ------------------------------------------------------------------------------ var _utils = load("res://addons/gut/utils.gd").get_instance() var _compare = _utils.Comparator.new() # constant for signal when calling yield_for const YIELD = "timeout" # Need a reference to the instance that is running the tests. This # is set by the gut class when it runs the tests. This gets you # access to the asserts in the tests you write. var gut = null var _disable_strict_datatype_checks = false # Holds all the text for a test's fail/pass. This is used for testing purposes # to see the text of a failed sub-test in test_test.gd var _fail_pass_text = [] const EDITOR_PROPERTY = PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_DEFAULT const VARIABLE_PROPERTY = PROPERTY_USAGE_SCRIPT_VARIABLE # Used with assert_setget enum { DEFAULT_SETTER_GETTER, SETTER_ONLY, GETTER_ONLY } # Summary counts for the test. var _summary = {asserts = 0, passed = 0, failed = 0, tests = 0, pending = 0} # This is used to watch signals so we can make assertions about them. var _signal_watcher = load("res://addons/gut/signal_watcher.gd").new() # Convenience copy of _utils.DOUBLE_STRATEGY var DOUBLE_STRATEGY = null var _lgr = _utils.get_logger() var _strutils = _utils.Strutils.new() # syntax sugar var ParameterFactory = _utils.ParameterFactory var CompareResult = _utils.CompareResult var InputFactory = _utils.InputFactory var InputSender = _utils.InputSender func _init(): DOUBLE_STRATEGY = _utils.DOUBLE_STRATEGY # yes, this is right func _str(thing): return _strutils.type2str(thing) # ------------------------------------------------------------------------------ # Fail an assertion. Causes test and script to fail as well. # ------------------------------------------------------------------------------ func _fail(text): _summary.asserts += 1 _summary.failed += 1 _fail_pass_text.append("failed: " + text) if gut: _lgr.failed(text) gut._fail(text) # ------------------------------------------------------------------------------ # Pass an assertion. # ------------------------------------------------------------------------------ func _pass(text): _summary.asserts += 1 _summary.passed += 1 _fail_pass_text.append("passed: " + text) if gut: _lgr.passed(text) gut._pass(text) # ------------------------------------------------------------------------------ # Checks if the datatypes passed in match. If they do not then this will cause # a fail to occur. If they match then TRUE is returned, FALSE if not. This is # used in all the assertions that compare values. # ------------------------------------------------------------------------------ func _do_datatypes_match__fail_if_not(got, expected, text): var did_pass = true if !_disable_strict_datatype_checks: var got_type = typeof(got) var expect_type = typeof(expected) if got_type != expect_type and got != null and expected != null: # If we have a mismatch between float and int (types 2 and 3) then # print out a warning but do not fail. if [2, 3].has(got_type) and [2, 3].has(expect_type): _lgr.warn( str( "Warn: Float/Int comparison. Got ", _strutils.types[got_type], " but expected ", _strutils.types[expect_type] ) ) else: _fail( ( "Cannot compare " + _strutils.types[got_type] + "[" + _str(got) + "] to " + _strutils.types[expect_type] + "[" + _str(expected) + "]. " + text ) ) did_pass = false return did_pass # ------------------------------------------------------------------------------ # Create a string that lists all the methods that were called on an spied # instance. # ------------------------------------------------------------------------------ func _get_desc_of_calls_to_instance(inst): var BULLET = " * " var calls = gut.get_spy().get_call_list_as_string(inst) # indent all the calls calls = BULLET + calls.replace("\n", "\n" + BULLET) # remove trailing newline and bullet calls = calls.substr(0, calls.length() - BULLET.length() - 1) return "Calls made on " + str(inst) + "\n" + calls # ------------------------------------------------------------------------------ # Signal assertion helper. Do not call directly, use _can_make_signal_assertions # ------------------------------------------------------------------------------ func _fail_if_does_not_have_signal(object, signal_name): var did_fail = false if !_signal_watcher.does_object_have_signal(object, signal_name): _fail(str("Object ", object, " does not have the signal [", signal_name, "]")) did_fail = true return did_fail # ------------------------------------------------------------------------------ # Signal assertion helper. Do not call directly, use _can_make_signal_assertions # ------------------------------------------------------------------------------ func _fail_if_not_watching(object): var did_fail = false if !_signal_watcher.is_watching_object(object): _fail( str( "Cannot make signal assertions because the object ", object, " is not being watched. Call watch_signals(some_object) to be able to make assertions about signals." ) ) did_fail = true return did_fail # ------------------------------------------------------------------------------ # Returns text that contains original text and a list of all the signals that # were emitted for the passed in object. # ------------------------------------------------------------------------------ func _get_fail_msg_including_emitted_signals(text, object): return str(text, " (Signals emitted: ", _signal_watcher.get_signals_emitted(object), ")") # ------------------------------------------------------------------------------ # This validates that parameters is an array and generates a specific error # and a failure with a specific message # ------------------------------------------------------------------------------ func _fail_if_parameters_not_array(parameters): var invalid = parameters != null and typeof(parameters) != TYPE_ARRAY if invalid: _lgr.error('The "parameters" parameter must be an array of expected parameter values.') _fail("Cannot compare paramter values because an array was not passed.") return invalid func _create_obj_from_type(type): var obj = null if type.is_class("PackedScene"): obj = type.instance() add_child(obj) else: obj = type.new() return obj # ####################### # Virtual Methods # ####################### # alias for prerun_setup func before_all(): pass # alias for setup func before_each(): pass # alias for postrun_teardown func after_all(): pass # alias for teardown func after_each(): pass # ####################### # Public # ####################### func get_logger(): return _lgr func set_logger(logger): _lgr = logger # ####################### # Asserts # ####################### # ------------------------------------------------------------------------------ # Asserts that the expected value equals the value got. # ------------------------------------------------------------------------------ func assert_eq(got, expected, text = ""): if _do_datatypes_match__fail_if_not(got, expected, text): var disp = "[" + _str(got) + "] expected to equal [" + _str(expected) + "]: " + text var result = null if typeof(got) == TYPE_ARRAY: result = _compare.shallow(got, expected) else: result = _compare.simple(got, expected) if typeof(got) in [TYPE_ARRAY, TYPE_DICTIONARY]: disp = str(result.summary, " ", text) if result.are_equal: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts that the value got does not equal the "not expected" value. # ------------------------------------------------------------------------------ func assert_ne(got, not_expected, text = ""): if _do_datatypes_match__fail_if_not(got, not_expected, text): var disp = ( "[" + _str(got) + "] expected to not equal [" + _str(not_expected) + "]: " + text ) var result = null if typeof(got) == TYPE_ARRAY: result = _compare.shallow(got, not_expected) else: result = _compare.simple(got, not_expected) if typeof(got) in [TYPE_ARRAY, TYPE_DICTIONARY]: disp = str(result.summary, " ", text) if result.are_equal: _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Asserts that the expected value almost equals the value got. # ------------------------------------------------------------------------------ func assert_almost_eq(got, expected, error_interval, text = ""): var disp = ( "[" + _str(got) + "] expected to equal [" + _str(expected) + "] +/- [" + str(error_interval) + "]: " + text ) if ( _do_datatypes_match__fail_if_not(got, expected, text) and _do_datatypes_match__fail_if_not(got, error_interval, text) ): if not _is_almost_eq(got, expected, error_interval): _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Asserts that the expected value does not almost equal the value got. # ------------------------------------------------------------------------------ func assert_almost_ne(got, not_expected, error_interval, text = ""): var disp = ( "[" + _str(got) + "] expected to not equal [" + _str(not_expected) + "] +/- [" + str(error_interval) + "]: " + text ) if ( _do_datatypes_match__fail_if_not(got, not_expected, text) and _do_datatypes_match__fail_if_not(got, error_interval, text) ): if _is_almost_eq(got, not_expected, error_interval): _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Helper function which correctly compares two variables, # while properly handling vector2/3 types # ------------------------------------------------------------------------------ func _is_almost_eq(got, expected, error_interval) -> bool: var result = false if typeof(got) == TYPE_VECTOR2: if got.x >= (expected.x - error_interval.x) and got.x <= (expected.x + error_interval.x): if ( got.y >= (expected.y - error_interval.y) and got.y <= (expected.y + error_interval.y) ): result = true elif typeof(got) == TYPE_VECTOR3: if got.x >= (expected.x - error_interval.x) and got.x <= (expected.x + error_interval.x): if ( got.y >= (expected.y - error_interval.y) and got.y <= (expected.y + error_interval.y) ): if ( got.z >= (expected.z - error_interval.z) and got.z <= (expected.z + error_interval.z) ): result = true elif got >= (expected - error_interval) and got <= (expected + error_interval): result = true return result # ------------------------------------------------------------------------------ # Asserts got is greater than expected # ------------------------------------------------------------------------------ func assert_gt(got, expected, text = ""): var disp = "[" + _str(got) + "] expected to be > than [" + _str(expected) + "]: " + text if _do_datatypes_match__fail_if_not(got, expected, text): if got > expected: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts got is less than expected # ------------------------------------------------------------------------------ func assert_lt(got, expected, text = ""): var disp = "[" + _str(got) + "] expected to be < than [" + _str(expected) + "]: " + text if _do_datatypes_match__fail_if_not(got, expected, text): if got < expected: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # asserts that got is true # ------------------------------------------------------------------------------ func assert_true(got, text = ""): if typeof(got) == TYPE_BOOL: if got: _pass(text) else: _fail(text) else: var msg = str("Cannot convert ", _strutils.type2str(got), " to boolean") _fail(msg) # ------------------------------------------------------------------------------ # Asserts that got is false # ------------------------------------------------------------------------------ func assert_false(got, text = ""): if typeof(got) == TYPE_BOOL: if got: _fail(text) else: _pass(text) else: var msg = str("Cannot convert ", _strutils.type2str(got), " to boolean") _fail(msg) # ------------------------------------------------------------------------------ # Asserts value is between (inclusive) the two expected values. # ------------------------------------------------------------------------------ func assert_between(got, expect_low, expect_high, text = ""): var disp = ( "[" + _str(got) + "] expected to be between [" + _str(expect_low) + "] and [" + str(expect_high) + "]: " + text ) if ( _do_datatypes_match__fail_if_not(got, expect_low, text) and _do_datatypes_match__fail_if_not(got, expect_high, text) ): if expect_low > expect_high: disp = ( "INVALID range. [" + str(expect_low) + "] is not less than [" + str(expect_high) + "]" ) _fail(disp) else: if got < expect_low or got > expect_high: _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Asserts value is not between (exclusive) the two expected values. # ------------------------------------------------------------------------------ func assert_not_between(got, expect_low, expect_high, text = ""): var disp = ( "[" + _str(got) + "] expected not to be between [" + _str(expect_low) + "] and [" + str(expect_high) + "]: " + text ) if ( _do_datatypes_match__fail_if_not(got, expect_low, text) and _do_datatypes_match__fail_if_not(got, expect_high, text) ): if expect_low > expect_high: disp = ( "INVALID range. [" + str(expect_low) + "] is not less than [" + str(expect_high) + "]" ) _fail(disp) else: if got > expect_low and got < expect_high: _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Uses the 'has' method of the object passed in to determine if it contains # the passed in element. # ------------------------------------------------------------------------------ func assert_has(obj, element, text = ""): var disp = str("Expected [", _str(obj), "] to contain value: [", _str(element), "]: ", text) if obj.has(element): _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func assert_does_not_have(obj, element, text = ""): var disp = str( "Expected [", _str(obj), "] to NOT contain value: [", _str(element), "]: ", text ) if obj.has(element): _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Asserts that a file exists # ------------------------------------------------------------------------------ func assert_file_exists(file_path): var disp = "expected [" + file_path + "] to exist." var f = File.new() if f.file_exists(file_path): _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts that a file should not exist # ------------------------------------------------------------------------------ func assert_file_does_not_exist(file_path): var disp = "expected [" + file_path + "] to NOT exist" var f = File.new() if !f.file_exists(file_path): _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts the specified file is empty # ------------------------------------------------------------------------------ func assert_file_empty(file_path): var disp = "expected [" + file_path + "] to be empty" var f = File.new() if f.file_exists(file_path) and gut.is_file_empty(file_path): _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts the specified file is not empty # ------------------------------------------------------------------------------ func assert_file_not_empty(file_path): var disp = "expected [" + file_path + "] to contain data" if !gut.is_file_empty(file_path): _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts the object has the specified method # ------------------------------------------------------------------------------ func assert_has_method(obj, method, text = ""): var disp = _str(obj) + " should have method: " + method if text != "": disp = _str(obj) + " " + text assert_true(obj.has_method(method), disp) # Old deprecated method name func assert_get_set_methods(obj, property, default, set_to): _lgr.deprecated("assert_get_set_methods", "assert_accessors") assert_accessors(obj, property, default, set_to) # ------------------------------------------------------------------------------ # Verifies the object has get and set methods for the property passed in. The # property isn't tied to anything, just a name to be appended to the end of # get_ and set_. Asserts the get_ and set_ methods exist, if not, it stops there. # If they exist then it asserts get_ returns the expected default then calls # set_ and asserts get_ has the value it was set to. # ------------------------------------------------------------------------------ func assert_accessors(obj, property, default, set_to): var fail_count = _summary.failed var get_func = "get_" + property var set_func = "set_" + property if obj.has_method("is_" + property): get_func = "is_" + property assert_has_method(obj, get_func, "should have getter starting with get_ or is_") assert_has_method(obj, set_func) # SHORT CIRCUIT if _summary.failed > fail_count: return assert_eq(obj.call(get_func), default, "It should have the expected default value.") obj.call(set_func, set_to) assert_eq(obj.call(get_func), set_to, "The set value should have been returned.") # --------------------------------------------------------------------------- # Property search helper. Used to retrieve Dictionary of specified property # from passed object. Returns null if not found. # If provided, property_usage constrains the type of property returned by # passing either: # EDITOR_PROPERTY for properties defined as: export(int) var some_value # VARIABLE_PROPERTY for properties defined as: var another_value # --------------------------------------------------------------------------- func _find_object_property(obj, property_name, property_usage = null): var result = null var found = false var properties = obj.get_property_list() while !found and !properties.empty(): var property = properties.pop_back() if property["name"] == property_name: if property_usage == null or property["usage"] == property_usage: result = property found = true return result # ------------------------------------------------------------------------------ # Asserts a class exports a variable. # ------------------------------------------------------------------------------ func assert_exports(obj, property_name, type): var disp = "expected %s to have editor property [%s]" % [_str(obj), property_name] var property = _find_object_property(obj, property_name, EDITOR_PROPERTY) if property != null: disp += ( " of type [%s]. Got type [%s]." % [_strutils.types[type], _strutils.types[property["type"]]] ) if property["type"] == type: _pass(disp) else: _fail(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Signal assertion helper. # # Verifies that the object and signal are valid for making signal assertions. # This will fail with specific messages that indicate why they are not valid. # This returns true/false to indicate if the object and signal are valid. # ------------------------------------------------------------------------------ func _can_make_signal_assertions(object, signal_name): return !(_fail_if_not_watching(object) or _fail_if_does_not_have_signal(object, signal_name)) # ------------------------------------------------------------------------------ # Check if an object is connected to a signal on another object. Returns True # if it is and false otherwise # ------------------------------------------------------------------------------ func _is_connected(signaler_obj, connect_to_obj, signal_name, method_name = ""): if method_name != "": return signaler_obj.is_connected(signal_name, connect_to_obj, method_name) else: var connections = signaler_obj.get_signal_connection_list(signal_name) for conn in connections: if (conn.source == signaler_obj) and (conn.target == connect_to_obj): return true return false # ------------------------------------------------------------------------------ # Watch the signals for an object. This must be called before you can make # any assertions about the signals themselves. # ------------------------------------------------------------------------------ func watch_signals(object): _signal_watcher.watch_signals(object) # ------------------------------------------------------------------------------ # Asserts that an object is connected to a signal on another object # # This will fail with specific messages if the target object is not connected # to the specified signal on the source object. # ------------------------------------------------------------------------------ func assert_connected(signaler_obj, connect_to_obj, signal_name, method_name = ""): pass var method_disp = "" if method_name != "": method_disp = str(" using method: [", method_name, "] ") var disp = str( "Expected object ", _str(signaler_obj), " to be connected to signal: [", signal_name, "] on ", _str(connect_to_obj), method_disp ) if _is_connected(signaler_obj, connect_to_obj, signal_name, method_name): _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts that an object is not connected to a signal on another object # # This will fail with specific messages if the target object is connected # to the specified signal on the source object. # ------------------------------------------------------------------------------ func assert_not_connected(signaler_obj, connect_to_obj, signal_name, method_name = ""): var method_disp = "" if method_name != "": method_disp = str(" using method: [", method_name, "] ") var disp = str( "Expected object ", _str(signaler_obj), " to not be connected to signal: [", signal_name, "] on ", _str(connect_to_obj), method_disp ) if _is_connected(signaler_obj, connect_to_obj, signal_name, method_name): _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Asserts that a signal has been emitted at least once. # # This will fail with specific messages if the object is not being watched or # the object does not have the specified signal # ------------------------------------------------------------------------------ func assert_signal_emitted(object, signal_name, text = ""): var disp = str( "Expected object ", _str(object), " to have emitted signal [", signal_name, "]: ", text ) if _can_make_signal_assertions(object, signal_name): if _signal_watcher.did_emit(object, signal_name): _pass(disp) else: _fail(_get_fail_msg_including_emitted_signals(disp, object)) # ------------------------------------------------------------------------------ # Asserts that a signal has not been emitted. # # This will fail with specific messages if the object is not being watched or # the object does not have the specified signal # ------------------------------------------------------------------------------ func assert_signal_not_emitted(object, signal_name, text = ""): var disp = str( "Expected object ", _str(object), " to NOT emit signal [", signal_name, "]: ", text ) if _can_make_signal_assertions(object, signal_name): if _signal_watcher.did_emit(object, signal_name): _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Asserts that a signal was fired with the specified parameters. The expected # parameters should be passed in as an array. An optional index can be passed # when a signal has fired more than once. The default is to retrieve the most # recent emission of the signal. # # This will fail with specific messages if the object is not being watched or # the object does not have the specified signal # ------------------------------------------------------------------------------ func assert_signal_emitted_with_parameters(object, signal_name, parameters, index = -1): if typeof(parameters) != TYPE_ARRAY: _lgr.error( "The expected parameters must be wrapped in an array, you passed: " + _str(parameters) ) _fail("Bad Parameters") return var disp = str( "Expected object ", _str(object), " to emit signal [", signal_name, "] with parameters ", parameters, ", got " ) if _can_make_signal_assertions(object, signal_name): if _signal_watcher.did_emit(object, signal_name): var parms_got = _signal_watcher.get_signal_parameters(object, signal_name, index) var diff_result = _compare.deep(parameters, parms_got) if diff_result.are_equal(): _pass(str(disp, parms_got)) else: _fail( str( "Expected object ", _str(object), " to emit signal [", signal_name, "] with parameters ", diff_result.summarize() ) ) else: var text = str("Object ", object, " did not emit signal [", signal_name, "]") _fail(_get_fail_msg_including_emitted_signals(text, object)) # ------------------------------------------------------------------------------ # Assert that a signal has been emitted a specific number of times. # # This will fail with specific messages if the object is not being watched or # the object does not have the specified signal # ------------------------------------------------------------------------------ func assert_signal_emit_count(object, signal_name, times, text = ""): if _can_make_signal_assertions(object, signal_name): var count = _signal_watcher.get_emit_count(object, signal_name) var disp = str( "Expected the signal [", signal_name, "] emit count of [", count, "] to equal [", times, "]: ", text ) if count == times: _pass(disp) else: _fail(_get_fail_msg_including_emitted_signals(disp, object)) # ------------------------------------------------------------------------------ # Assert that the passed in object has the specified signal # ------------------------------------------------------------------------------ func assert_has_signal(object, signal_name, text = ""): var disp = str("Expected object ", _str(object), " to have signal [", signal_name, "]: ", text) if _signal_watcher.does_object_have_signal(object, signal_name): _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Returns the number of times a signal was emitted. -1 returned if the object # is not being watched. # ------------------------------------------------------------------------------ func get_signal_emit_count(object, signal_name): return _signal_watcher.get_emit_count(object, signal_name) # ------------------------------------------------------------------------------ # Get the parmaters of a fired signal. If the signal was not fired null is # returned. You can specify an optional index (use get_signal_emit_count to # determine the number of times it was emitted). The default index is the # latest time the signal was fired (size() -1 insetead of 0). The parameters # returned are in an array. # ------------------------------------------------------------------------------ func get_signal_parameters(object, signal_name, index = -1): return _signal_watcher.get_signal_parameters(object, signal_name, index) # ------------------------------------------------------------------------------ # Get the parameters for a method call to a doubled object. By default it will # return the most recent call. You can optionally specify an index. # # Returns: # * an array of parameter values if a call the method was found # * null when a call to the method was not found or the index specified was # invalid. # ------------------------------------------------------------------------------ func get_call_parameters(object, method_name, index = -1): var to_return = null if _utils.is_double(object): to_return = gut.get_spy().get_call_parameters(object, method_name, index) else: _lgr.error("You must pass a doulbed object to get_call_parameters.") return to_return # ------------------------------------------------------------------------------ # Returns the call count for a method with optional paramter matching. # ------------------------------------------------------------------------------ func get_call_count(object, method_name, parameters = null): return gut.get_spy().call_count(object, method_name, parameters) # ------------------------------------------------------------------------------ # Assert that object is an instance of a_class # ------------------------------------------------------------------------------ func assert_extends(object, a_class, text = ""): _lgr.deprecated("assert_extends", "assert_is") assert_is(object, a_class, text) # Alias for assert_extends func assert_is(object, a_class, text = ""): var disp = "" #var disp = str('Expected [', _str(object), '] to be type of [', a_class, ']: ', text) var NATIVE_CLASS = "GDScriptNativeClass" var GDSCRIPT_CLASS = "GDScript" var bad_param_2 = "Parameter 2 must be a Class (like Node2D or Label). You passed " if typeof(object) != TYPE_OBJECT: _fail(str("Parameter 1 must be an instance of an object. You passed: ", _str(object))) elif typeof(a_class) != TYPE_OBJECT: _fail(str(bad_param_2, _str(a_class))) else: var a_str = _str(a_class) disp = str("Expected [", _str(object), "] to extend [", a_str, "]: ", text) if a_class.get_class() != NATIVE_CLASS and a_class.get_class() != GDSCRIPT_CLASS: _fail(str(bad_param_2, a_str)) else: if object is a_class: _pass(disp) else: _fail(disp) func _get_typeof_string(the_type): var to_return = "" if _strutils.types.has(the_type): to_return += str(the_type, "(", _strutils.types[the_type], ")") else: to_return += str(the_type) return to_return # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func assert_typeof(object, type, text = ""): var disp = str("Expected [typeof(", object, ") = ") disp += _get_typeof_string(typeof(object)) disp += "] to equal [" disp += _get_typeof_string(type) + "]" disp += ". " + text if typeof(object) == type: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func assert_not_typeof(object, type, text = ""): var disp = str("Expected [typeof(", object, ") = ") disp += _get_typeof_string(typeof(object)) disp += "] to not equal [" disp += _get_typeof_string(type) + "]" disp += ". " + text if typeof(object) != type: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Assert that text contains given search string. # The match_case flag determines case sensitivity. # ------------------------------------------------------------------------------ func assert_string_contains(text, search, match_case = true): var empty_search = "Expected text and search strings to be non-empty. You passed '%s' and '%s'." var disp = "Expected '%s' to contain '%s', match_case=%s" % [text, search, match_case] if text == "" or search == "": _fail(empty_search % [text, search]) elif match_case: if text.find(search) == -1: _fail(disp) else: _pass(disp) else: if text.to_lower().find(search.to_lower()) == -1: _fail(disp) else: _pass(disp) # ------------------------------------------------------------------------------ # Assert that text starts with given search string. # match_case flag determines case sensitivity. # ------------------------------------------------------------------------------ func assert_string_starts_with(text, search, match_case = true): var empty_search = "Expected text and search strings to be non-empty. You passed '%s' and '%s'." var disp = "Expected '%s' to start with '%s', match_case=%s" % [text, search, match_case] if text == "" or search == "": _fail(empty_search % [text, search]) elif match_case: if text.find(search) == 0: _pass(disp) else: _fail(disp) else: if text.to_lower().find(search.to_lower()) == 0: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Assert that text ends with given search string. # match_case flag determines case sensitivity. # ------------------------------------------------------------------------------ func assert_string_ends_with(text, search, match_case = true): var empty_search = "Expected text and search strings to be non-empty. You passed '%s' and '%s'." var disp = "Expected '%s' to end with '%s', match_case=%s" % [text, search, match_case] var required_index = len(text) - len(search) if text == "" or search == "": _fail(empty_search % [text, search]) elif match_case: if text.find(search) == required_index: _pass(disp) else: _fail(disp) else: if text.to_lower().find(search.to_lower()) == required_index: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Assert that a method was called on an instance of a doubled class. If # parameters are supplied then the params passed in when called must match. # TODO make 3rd parameter "param_or_text" and add fourth parameter of "text" and # then work some magic so this can have a "text" parameter without being # annoying. # ------------------------------------------------------------------------------ func assert_called(inst, method_name, parameters = null): var disp = str("Expected [", method_name, "] to have been called on ", _str(inst)) if _fail_if_parameters_not_array(parameters): return if !_utils.is_double(inst): _fail( "You must pass a doubled instance to assert_called. Check the wiki for info on using double." ) else: if gut.get_spy().was_called(inst, method_name, parameters): _pass(disp) else: if parameters != null: disp += str(" with parameters ", parameters) _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) # ------------------------------------------------------------------------------ # Assert that a method was not called on an instance of a doubled class. If # parameters are specified then this will only fail if it finds a call that was # sent matching parameters. # ------------------------------------------------------------------------------ func assert_not_called(inst, method_name, parameters = null): var disp = str("Expected [", method_name, "] to NOT have been called on ", _str(inst)) if _fail_if_parameters_not_array(parameters): return if !_utils.is_double(inst): _fail( "You must pass a doubled instance to assert_not_called. Check the wiki for info on using double." ) else: if gut.get_spy().was_called(inst, method_name, parameters): if parameters != null: disp += str(" with parameters ", parameters) _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) else: _pass(disp) # ------------------------------------------------------------------------------ # Assert that a method on an instance of a doubled class was called a number # of times. If parameters are specified then only calls with matching # parameter values will be counted. # ------------------------------------------------------------------------------ func assert_call_count(inst, method_name, expected_count, parameters = null): var count = gut.get_spy().call_count(inst, method_name, parameters) if _fail_if_parameters_not_array(parameters): return var param_text = "" if parameters: param_text = " with parameters " + str(parameters) var disp = "Expected [%s] on %s to be called [%s] times%s. It was called [%s] times." disp = disp % [method_name, _str(inst), expected_count, param_text, count] if !_utils.is_double(inst): _fail( "You must pass a doubled instance to assert_call_count. Check the wiki for info on using double." ) else: if count == expected_count: _pass(disp) else: _fail(str(disp, "\n", _get_desc_of_calls_to_instance(inst))) # ------------------------------------------------------------------------------ # Asserts the passed in value is null # ------------------------------------------------------------------------------ func assert_null(got, text = ""): var disp = str("Expected [", _str(got), "] to be NULL: ", text) if got == null: _pass(disp) else: _fail(disp) # ------------------------------------------------------------------------------ # Asserts the passed in value is null # ------------------------------------------------------------------------------ func assert_not_null(got, text = ""): var disp = str("Expected [", _str(got), "] to be anything but NULL: ", text) if got == null: _fail(disp) else: _pass(disp) # ----------------------------------------------------------------------------- # Asserts object has been freed from memory # We pass in a title (since if it is freed, we lost all identity data) # ----------------------------------------------------------------------------- func assert_freed(obj, title = "something"): var disp = title if is_instance_valid(obj): disp = _strutils.type2str(obj) + title assert_true(not is_instance_valid(obj), "Expected [%s] to be freed" % disp) # ------------------------------------------------------------------------------ # Asserts Object has not been freed from memory # ----------------------------------------------------------------------------- func assert_not_freed(obj, title): var disp = title if is_instance_valid(obj): disp = _strutils.type2str(obj) + title assert_true(is_instance_valid(obj), "Expected [%s] to not be freed" % disp) # ------------------------------------------------------------------------------ # Asserts that the current test has not introduced any new orphans. This only # applies to the test code that preceedes a call to this method so it should be # the last thing your test does. # ------------------------------------------------------------------------------ func assert_no_new_orphans(text = ""): var count = gut.get_orphan_counter().get_counter("test") var msg = "" if text != "": msg = ": " + text # Note that get_counter will return -1 if the counter does not exist. This # can happen with a misplaced assert_no_new_orphans. Checking for > 0 # ensures this will not cause some weird failure. if count > 0: _fail(str("Expected no orphans, but found ", count, msg)) else: _pass("No new orphans found." + msg) # ------------------------------------------------------------------------------ # Returns a dictionary that contains # - an is_valid flag whether validation was successful or not and # - a message that gives some information about the validation errors. # ------------------------------------------------------------------------------ func _validate_assert_setget_called_input(type, name_property, name_setter, name_getter): var obj = null var result = {"is_valid": true, "msg": ""} if null == type or typeof(type) != TYPE_OBJECT or not type.is_class("Resource"): result.is_valid = false result.msg = str("The type parameter should be a ressource, ", _str(type), " was passed.") return result if null == double(type): result.is_valid = false result.msg = str( "Attempt to double the type parameter failed. The type parameter should be a ressource that can be doubled." ) return result obj = _create_obj_from_type(type) var property = _find_object_property(obj, str(name_property)) if null == property: result.is_valid = false result.msg += str("The property %s does not exist." % _str(name_property)) if name_setter == "" and name_getter == "": result.is_valid = false result.msg += str("Either setter or getter method must be specified.") if name_setter != "" and not obj.has_method(str(name_setter)): result.is_valid = false result.msg += str("Setter method %s does not exist. " % _str(name_setter)) if name_getter != "" and not obj.has_method(str(name_getter)): result.is_valid = false result.msg += str("Getter method %s does not exist. " % _str(name_getter)) obj.free() return result # ------------------------------------------------------------------------------ # Validates the singleton_name is a string and exists. Errors when conditions # are not met. Returns true/false if singleton_name is valid or not. # ------------------------------------------------------------------------------ func _validate_singleton_name(singleton_name): var is_valid = true if typeof(singleton_name) != TYPE_STRING: _lgr.error( "double_singleton requires a Godot singleton name, you passed " + _str(singleton_name) ) is_valid = false # Sometimes they have underscores in front of them, sometimes they do not. # The doubler is smart enought of ind the right thing, so this has to be # that smart as well. elif !ClassDB.class_exists(singleton_name) and !ClassDB.class_exists("_" + singleton_name): var txt = str( "The singleton [", singleton_name, "] could not be found. ", "Check the GlobalScope page for a list of singletons." ) _lgr.error(txt) is_valid = false return is_valid # ------------------------------------------------------------------------------ # Asserts the given setter and getter methods are called when the given property # is accessed. # ------------------------------------------------------------------------------ func _assert_setget_called(type, name_property, setter = "", getter = ""): var name_setter = _utils.nvl(setter, "") var name_getter = _utils.nvl(getter, "") var validation = _validate_assert_setget_called_input( type, name_property, str(name_setter), str(name_getter) ) if not validation.is_valid: _fail(validation.msg) return var message = "" var amount_calls_setter = 0 var amount_calls_getter = 0 var expected_calls_setter = 0 var expected_calls_getter = 0 var obj = _create_obj_from_type(double(type)) if name_setter != "": expected_calls_setter = 1 stub(obj, name_setter).to_do_nothing() obj.set(name_property, null) amount_calls_setter = gut.get_spy().call_count(obj, str(name_setter)) if name_getter != "": expected_calls_getter = 1 stub(obj, name_getter).to_do_nothing() var __new_property = obj.get(name_property) amount_calls_getter = gut.get_spy().call_count(obj, str(name_getter)) obj.free() # assert if ( amount_calls_setter == expected_calls_setter and amount_calls_getter == expected_calls_getter ): _pass(str("setget for %s is correctly configured." % _str(name_property))) else: if amount_calls_setter < expected_calls_setter: message += " The setter was not called." elif amount_calls_setter > expected_calls_setter: message += " The setter was called but should not have been." if amount_calls_getter < expected_calls_getter: message += " The getter was not called." elif amount_calls_getter > expected_calls_getter: message += " The getter was called but should not have been." _fail(str(message)) # ------------------------------------------------------------------------------ # Wrapper: invokes assert_setget_called but provides a slightly more convenient # signature # ------------------------------------------------------------------------------ func assert_setget( instance, name_property, const_or_setter = DEFAULT_SETTER_GETTER, getter = "__not_set__" ): var getter_name = null if getter != "__not_set__": getter_name = getter var setter_name = null if typeof(const_or_setter) == TYPE_INT: if const_or_setter in [SETTER_ONLY, DEFAULT_SETTER_GETTER]: setter_name = str("set_", name_property) if const_or_setter in [GETTER_ONLY, DEFAULT_SETTER_GETTER]: getter_name = str("get_", name_property) else: setter_name = const_or_setter var resource = null if instance.is_class("Resource"): resource = instance else: resource = instance.get_script() _assert_setget_called(resource, str(name_property), setter_name, getter_name) # ------------------------------------------------------------------------------ # Wrapper: asserts if the property exists, the accessor methods exist and the # setget keyword is set for accessor methods # ------------------------------------------------------------------------------ func assert_property(instance, name_property, default_value, new_value) -> void: var free_me = [] var resource = null var obj = null if instance.is_class("Resource"): resource = instance obj = _create_obj_from_type(resource) free_me.append(obj) else: resource = instance.get_script() obj = instance var name_setter = "set_" + str(name_property) var name_getter = "get_" + str(name_property) var pre_fail_count = get_fail_count() assert_accessors(obj, str(name_property), default_value, new_value) _assert_setget_called(resource, str(name_property), name_setter, name_getter) for entry in free_me: entry.free() # assert if get_fail_count() == pre_fail_count: _pass(str("The property is set up as expected.")) else: _fail(str("The property is not set up as expected. Examine subtests to see what failed.")) # ------------------------------------------------------------------------------ # Mark the current test as pending. # ------------------------------------------------------------------------------ func pending(text = ""): _summary.pending += 1 if gut: _lgr.pending(text) gut._pending(text) # ------------------------------------------------------------------------------ # Returns the number of times a signal was emitted. -1 returned if the object # is not being watched. # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Yield for the time sent in. The optional message will be printed when # Gut detects the yield. When the time expires the YIELD signal will be # emitted. # ------------------------------------------------------------------------------ func yield_for(time, msg = ""): return gut.set_yield_time(time, msg) # ------------------------------------------------------------------------------ # Yield to a signal or a maximum amount of time, whichever comes first. When # the conditions are met the YIELD signal will be emitted. # ------------------------------------------------------------------------------ func yield_to(obj, signal_name, max_wait, msg = ""): watch_signals(obj) gut.set_yield_signal_or_time(obj, signal_name, max_wait, msg) return gut # ------------------------------------------------------------------------------ # Yield for a number of frames. The optional message will be printed. when # Gut detects a yield. When the number of frames have elapsed (counted in gut's # _process function) the YIELD signal will be emitted. # ------------------------------------------------------------------------------ func yield_frames(frames, msg = ""): if frames <= 0: var text = str( "yeild_frames: frames must be > 0, you passed ", frames, ". 0 frames waited." ) _lgr.error(text) frames = 0 gut.set_yield_frames(frames, msg) return gut # ------------------------------------------------------------------------------ # Ends a test that had a yield in it. You only need to use this if you do # not make assertions after a yield. # ------------------------------------------------------------------------------ func end_test(): _lgr.deprecated("end_test is no longer necessary, you can remove it.") #gut.end_yielded_test() func get_summary(): return _summary func get_fail_count(): return _summary.failed func get_pass_count(): return _summary.passed func get_pending_count(): return _summary.pending func get_assert_count(): return _summary.asserts func clear_signal_watcher(): _signal_watcher.clear() func get_double_strategy(): return gut.get_doubler().get_strategy() func set_double_strategy(double_strategy): gut.get_doubler().set_strategy(double_strategy) func pause_before_teardown(): gut.pause_before_teardown() # ------------------------------------------------------------------------------ # Convert the _summary dictionary into text # ------------------------------------------------------------------------------ func get_summary_text(): var to_return = get_script().get_path() + "\n" to_return += str(" ", _summary.passed, " of ", _summary.asserts, " passed.") if _summary.pending > 0: to_return += str("\n ", _summary.pending, " pending") if _summary.failed > 0: to_return += str("\n ", _summary.failed, " failed.") return to_return # ------------------------------------------------------------------------------ # Double a script, inner class, or scene using a path or a loaded script/scene. # # # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func _smart_double(double_info): var override_strat = _utils.nvl(double_info.strategy, gut.get_doubler().get_strategy()) var to_return = null if double_info.is_scene(): if double_info.make_partial: to_return = gut.get_doubler().partial_double_scene(double_info.path, override_strat) else: to_return = gut.get_doubler().double_scene(double_info.path, override_strat) elif double_info.is_native(): if double_info.make_partial: to_return = gut.get_doubler().partial_double_gdnative(double_info.path) else: to_return = gut.get_doubler().double_gdnative(double_info.path) elif double_info.is_script(): if double_info.subpath == null: if double_info.make_partial: to_return = gut.get_doubler().partial_double(double_info.path, override_strat) else: to_return = gut.get_doubler().double(double_info.path, override_strat) else: if double_info.make_partial: to_return = gut.get_doubler().partial_double_inner( double_info.path, double_info.subpath, override_strat ) else: to_return = gut.get_doubler().double_inner( double_info.path, double_info.subpath, override_strat ) return to_return # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func double(thing, p2 = null, p3 = null): var double_info = DoubleInfo.new(thing, p2, p3) if !double_info.is_valid: _lgr.error("double requires a class or path, you passed an instance: " + _str(thing)) return null double_info.make_partial = false return _smart_double(double_info) # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ func partial_double(thing, p2 = null, p3 = null): var double_info = DoubleInfo.new(thing, p2, p3) if !double_info.is_valid: _lgr.error( "partial_double requires a class or path, you passed an instance: " + _str(thing) ) return null double_info.make_partial = true return _smart_double(double_info) # ------------------------------------------------------------------------------ # Doubles a Godot singleton # ------------------------------------------------------------------------------ func double_singleton(singleton_name): return null # var to_return = null # if(_validate_singleton_name(singleton_name)): # to_return = gut.get_doubler().double_singleton(singleton_name) # return to_return # ------------------------------------------------------------------------------ # Partial Doubles a Godot singleton # ------------------------------------------------------------------------------ func partial_double_singleton(singleton_name): return null # var to_return = null # if(_validate_singleton_name(singleton_name)): # to_return = gut.get_doubler().partial_double_singleton(singleton_name) # return to_return # ------------------------------------------------------------------------------ # Specifically double a scene # ------------------------------------------------------------------------------ func double_scene(path, strategy = null): var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) return gut.get_doubler().double_scene(path, override_strat) # ------------------------------------------------------------------------------ # Specifically double a script # ------------------------------------------------------------------------------ func double_script(path, strategy = null): var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) return gut.get_doubler().double(path, override_strat) # ------------------------------------------------------------------------------ # Specifically double an Inner class in a a script # ------------------------------------------------------------------------------ func double_inner(path, subpath, strategy = null): var override_strat = _utils.nvl(strategy, gut.get_doubler().get_strategy()) return gut.get_doubler().double_inner(path, subpath, override_strat) # ------------------------------------------------------------------------------ # Add a method that the doubler will ignore. You can pass this the path to a # script or scene or a loaded script or scene. When running tests, these # ignores are cleared after every test. # ------------------------------------------------------------------------------ func ignore_method_when_doubling(thing, method_name): var double_info = DoubleInfo.new(thing) var path = double_info.path if double_info.is_scene(): var inst = thing.instance() if inst.get_script(): path = inst.get_script().get_path() gut.get_doubler().add_ignored_method(path, method_name) # ------------------------------------------------------------------------------ # Stub something. # # Parameters # 1: the thing to stub, a file path or a instance or a class # 2: either an inner class subpath or the method name # 3: the method name if an inner class subpath was specified # NOTE: right now we cannot stub inner classes at the path level so this should # only be called with two parameters. I did the work though so I'm going # to leave it but not update the wiki. # ------------------------------------------------------------------------------ func stub(thing, p2, p3 = null): if _utils.is_instance(thing) and !_utils.is_double(thing): _lgr.error(str("You cannot use stub on ", _str(thing), " because it is not a double.")) return _utils.StubParams.new() var method_name = p2 var subpath = null if p3 != null: subpath = p2 method_name = p3 var sp = _utils.StubParams.new(thing, method_name, subpath) gut.get_stubber().add_stub(sp) return sp # ------------------------------------------------------------------------------ # convenience wrapper. # ------------------------------------------------------------------------------ func simulate(obj, times, delta): gut.simulate(obj, times, delta) # ------------------------------------------------------------------------------ # Replace the node at base_node.get_node(path) with with_this. All references # to the node via $ and get_node(...) will now return with_this. with_this will # get all the groups that the node that was replaced had. # # The node that was replaced is queued to be freed. # # TODO see replace_by method, this could simplify the logic here. # ------------------------------------------------------------------------------ func replace_node(base_node, path_or_node, with_this): var path = path_or_node if typeof(path_or_node) != TYPE_STRING: # This will cause an engine error if it fails. It always returns a # NodePath, even if it fails. Checking the name count is the only way # I found to check if it found something or not (after it worked I # didn't look any farther). path = base_node.get_path_to(path_or_node) if path.get_name_count() == 0: _lgr.error("You passed an object that base_node does not have. Cannot replace node.") return if !base_node.has_node(path): _lgr.error(str("Could not find node at path [", path, "]")) return var to_replace = base_node.get_node(path) var parent = to_replace.get_parent() var replace_name = to_replace.get_name() parent.remove_child(to_replace) parent.add_child(with_this) with_this.set_name(replace_name) with_this.set_owner(parent) var groups = to_replace.get_groups() for i in range(groups.size()): with_this.add_to_group(groups[i]) to_replace.queue_free() # ------------------------------------------------------------------------------ # This method does a somewhat complicated dance with Gut. It assumes that Gut # will clear its parameter handler after it finishes calling a parameterized test # enough times. # ------------------------------------------------------------------------------ func use_parameters(params): var ph = gut.get_parameter_handler() if ph == null: ph = _utils.ParameterHandler.new(params) gut.set_parameter_handler(ph) var output = str( "(call #", ph.get_call_count() + 1, ") with paramters: ", ph.get_current_parameters() ) _lgr.log(output) _lgr.inc_indent() return ph.next_parameters() # ------------------------------------------------------------------------------ # Marks whatever is passed in to be freed after the test finishes. It also # returns what is passed in so you can save a line of code. # var thing = autofree(Thing.new()) # ------------------------------------------------------------------------------ func autofree(thing): gut.get_autofree().add_free(thing) return thing # ------------------------------------------------------------------------------ # Works the same as autofree except queue_free will be called on the object # instead. This also imparts a brief pause after the test finishes so that # the queued object has time to free. # ------------------------------------------------------------------------------ func autoqfree(thing): gut.get_autofree().add_queue_free(thing) return thing # ------------------------------------------------------------------------------ # The same as autofree but it also adds the object as a child of the test. # ------------------------------------------------------------------------------ func add_child_autofree(node, legible_unique_name = false): gut.get_autofree().add_free(node) # Explicitly calling super here b/c add_child MIGHT change and I don't want # a bug sneaking its way in here. .add_child(node, legible_unique_name) return node # ------------------------------------------------------------------------------ # The same as autoqfree but it also adds the object as a child of the test. # ------------------------------------------------------------------------------ func add_child_autoqfree(node, legible_unique_name = false): gut.get_autofree().add_queue_free(node) # Explicitly calling super here b/c add_child MIGHT change and I don't want # a bug sneaking its way in here. .add_child(node, legible_unique_name) return node # ------------------------------------------------------------------------------ # Returns true if the test is passing as of the time of this call. False if not. # ------------------------------------------------------------------------------ func is_passing(): if ( gut.get_current_test_object() != null and !["before_all", "after_all"].has(gut.get_current_test_object().name) ): return ( gut.get_current_test_object().passed and gut.get_current_test_object().assert_count > 0 ) else: _lgr.error("No current test object found. is_passing must be called inside a test.") return null # ------------------------------------------------------------------------------ # Returns true if the test is failing as of the time of this call. False if not. # ------------------------------------------------------------------------------ func is_failing(): if ( gut.get_current_test_object() != null and !["before_all", "after_all"].has(gut.get_current_test_object().name) ): return !gut.get_current_test_object().passed else: _lgr.error("No current test object found. is_failing must be called inside a test.") return null # ------------------------------------------------------------------------------ # Marks the test as passing. Does not override any failing asserts or calls to # fail_test. Same as a passing assert. # ------------------------------------------------------------------------------ func pass_test(text): _pass(text) # ------------------------------------------------------------------------------ # Marks the test as failing. Same as a failing assert. # ------------------------------------------------------------------------------ func fail_test(text): _fail(text) # ------------------------------------------------------------------------------ # Peforms a deep compare on both values, a CompareResult instnace is returned. # The optional max_differences paramter sets the max_differences to be displayed. # ------------------------------------------------------------------------------ func compare_deep(v1, v2, max_differences = null): var result = _compare.deep(v1, v2) if max_differences != null: result.max_differences = max_differences return result # ------------------------------------------------------------------------------ # Peforms a shallow compare on both values, a CompareResult instnace is returned. # The optional max_differences paramter sets the max_differences to be displayed. # ------------------------------------------------------------------------------ func compare_shallow(v1, v2, max_differences = null): var result = _compare.shallow(v1, v2) if max_differences != null: result.max_differences = max_differences return result # ------------------------------------------------------------------------------ # Performs a deep compare and asserts the values are equal # ------------------------------------------------------------------------------ func assert_eq_deep(v1, v2): var result = compare_deep(v1, v2) if result.are_equal: _pass(result.get_short_summary()) else: _fail(result.summary) # ------------------------------------------------------------------------------ # Performs a deep compare and asserts the values are not equal # ------------------------------------------------------------------------------ func assert_ne_deep(v1, v2): var result = compare_deep(v1, v2) if !result.are_equal: _pass(result.get_short_summary()) else: _fail(result.get_short_summary()) # ------------------------------------------------------------------------------ # Performs a shallow compare and asserts the values are equal # ------------------------------------------------------------------------------ func assert_eq_shallow(v1, v2): var result = compare_shallow(v1, v2) if result.are_equal: _pass(result.get_short_summary()) else: _fail(result.summary) # ------------------------------------------------------------------------------ # Performs a shallow compare and asserts the values are not equal # ------------------------------------------------------------------------------ func assert_ne_shallow(v1, v2): var result = compare_shallow(v1, v2) if !result.are_equal: _pass(result.get_short_summary()) else: _fail(result.get_short_summary())