From 6fc7c17eade9e422497795ae3067666f02fc70c8 Mon Sep 17 00:00:00 2001 From: Butch Wesley Date: Wed, 24 Jul 2024 17:41:07 -0400 Subject: [PATCH] Gut 9 3 (#645) * version bump * more documentation * add cli changes to CHANGES * address flakey i578 tests * use verbose as default for no_print_logger * use new no_input_logger * fix issue with setting array values (CLI) * testing and documenting monitor switch failing tests * script parser no longer keeps reference to native instances * refactor skip script tests to use new dynamicness * add dedent to create_script_from_source * moved dynmaic gut script to its own file --- CHANGES.md | 8 +- README.md | 2 +- addons/gut/autofree.gd | 2 - addons/gut/cli/optparse.gd | 30 +++-- addons/gut/double_tools.gd | 2 +- addons/gut/dynamic_gdscript.gd | 2 +- addons/gut/input_sender.gd | 6 +- addons/gut/logger.gd | 5 +- addons/gut/plugin.cfg | 2 +- addons/gut/script_parser.gd | 22 +-- addons/gut/stub_params.gd | 3 + addons/gut/test.gd | 2 +- addons/gut/test_collector.gd | 2 +- addons/gut/utils.gd | 2 +- documentation/docs/Input-Sender.md | 2 + documentation/docs/conf.py | 4 +- documentation/docs/index.rst | 2 +- test/gut_test.gd | 15 ++- test/integration/test_gut_skip_scripts.gd | 157 ++++++++++------------ test/resources/dynamic_gut_test.gd | 69 ++++++++++ test/resources/wait_awhile.gd | 13 ++ test/unit/test_bugs/test_i578.gd | 47 +++++-- test/unit/test_collected_script.gd | 46 +++---- test/unit/test_input_sender.gd | 20 +-- test/unit/test_optparse.gd | 12 ++ test/unit/test_script_parser.gd | 7 +- test/unit/test_test.gd | 32 ++++- 27 files changed, 335 insertions(+), 181 deletions(-) create mode 100644 test/resources/dynamic_gut_test.gd create mode 100644 test/resources/wait_awhile.gd diff --git a/CHANGES.md b/CHANGES.md index 0a358258..318809cc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -# 9.2.2 +# 9.3.0 ## Features * You can Monkey Patch your doubles! You can make any method in a double call a specified `Callable` using `.to_call(callable)` on `stub`. Details are on the [Stubbing](https://gut.readthedocs.io/en/latest/Stubbing.html) wiki page. @@ -46,6 +46,11 @@ assert_true(await wait_for_signal(my_obj.my_singal, 2), func should_skip_script(): return EngineDebugger.is_active() ``` +* The CLI got an update to its Option Parser. There's more info in #623: + * options that take a value can now be specified with a space (`option value`) instead of using `option=value`. + * `-gh` option now has headings for the different options. It looks a lot better. + * `-gdir` and `-gtest` can be specified multiple times instead of using a comma delimited list. + * You can use `-gconfig=` to not use a config file. * Minor niceties such as showing that GUT is exiting in the title bar (takes a bit sometimes) and switching to full display at the end of a run if GUT does not automatically exit. ## Bug Fixes @@ -57,6 +62,7 @@ assert_true(await wait_for_signal(my_obj.my_singal, 2), ## Deprecations * The optional `GutTest` script variable `skip_script` has been deprecated. Use the new `should_skip_script` method instead. +* GUT now warns if you have overridden `_ready` in your test script without calling `super._ready`. diff --git a/README.md b/README.md index 6e9c903b..fc2ad4d9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ * GUT 7 on readthedocs.io: https://gut.readthedocs.io/en/godot_3x -# GUT 9.2.1 (Godot 4.2) +# GUT 9.3.0 (Godot 4.2) GUT (Godot Unit Test) is a unit testing framework for the [Godot Engine](https://godotengine.org/). It allows you to write tests for your gdscript in gdscript. GUT versions 9.x are for Godot 4.x diff --git a/addons/gut/autofree.gd b/addons/gut/autofree.gd index b82676bc..e35f7f57 100644 --- a/addons/gut/autofree.gd +++ b/addons/gut/autofree.gd @@ -55,5 +55,3 @@ func free_all(): if(is_instance_valid(_to_queue_free[i])): _to_queue_free[i].queue_free() _to_queue_free.clear() - - diff --git a/addons/gut/cli/optparse.gd b/addons/gut/cli/optparse.gd index 3b1fcc07..3c6433f6 100644 --- a/addons/gut/cli/optparse.gd +++ b/addons/gut/cli/optparse.gd @@ -129,18 +129,22 @@ #------------------------------------------------------------------------------- # Holds all the properties of a command line option +# +# value will return the default when it has not been set. #------------------------------------------------------------------------------- class Option: - static var empty_value = &'--__this_is_an_optparse_empty_value__--' - - var _value = empty_value - var value = empty_value: + var _has_been_set = false + var _value = null + # REMEMBER that when this option is an array, you have to set the value + # before you alter the contents of the array (append etc) or has_been_set + # will return false and it might not be used right. For example + # get_value_or_null will return null when you've actually changed the value. + var value = _value: get: - if(str(_value) == empty_value): - return default - else: - return _value + return _value + set(val): + _has_been_set = true _value = val var option_name = '' @@ -148,22 +152,22 @@ class Option: var description = '' var required = false + func _init(name,default_value,desc=''): option_name = name default = default_value description = desc - value = empty_value + _value = default func to_s(min_space=0): var subbed_desc = description - if(subbed_desc.find('[default]') != -1): - subbed_desc = subbed_desc.replace('[default]', str(default)) + subbed_desc = subbed_desc.replace('[default]', str(default)) return str(option_name.rpad(min_space), ' ', subbed_desc) func has_been_set(): - return str(_value) != empty_value + return _has_been_set @@ -323,6 +327,8 @@ func _set_option_value(option, raw_value): option.value = str(raw_value) elif(t == TYPE_ARRAY): var values = _convert_value_to_array(raw_value) + if(!option.has_been_set()): + option.value = [] option.value.append_array(values) elif(t == TYPE_BOOL): option.value = !option.default diff --git a/addons/gut/double_tools.gd b/addons/gut/double_tools.gd index e27838ed..186d432c 100644 --- a/addons/gut/double_tools.gd +++ b/addons/gut/double_tools.gd @@ -74,4 +74,4 @@ func vararg_warning(): "This method contains a vararg argument and the paramter count was not stubbed. " + \ "GUT adds extra parameters to this method which should fill most needs. " + \ "It is recommended that you stub param_count for this object's class to ensure " + \ - "that there are not any parameter count mismatch errors.") \ No newline at end of file + "that there are not any parameter count mismatch errors.") diff --git a/addons/gut/dynamic_gdscript.gd b/addons/gut/dynamic_gdscript.gd index 94789def..e3e19462 100644 --- a/addons/gut/dynamic_gdscript.gd +++ b/addons/gut/dynamic_gdscript.gd @@ -15,7 +15,7 @@ func create_script_from_source(source, override_path=null): r_path = override_path var DynamicScript = GDScript.new() - DynamicScript.source_code = source + DynamicScript.source_code = source.dedent() # The resource_path must be unique or Godot thinks it is trying # to load something it has already loaded and generates an error like # ERROR: Another resource is loaded from path 'workaround for godot diff --git a/addons/gut/input_sender.gd b/addons/gut/input_sender.gd index a80a704f..1c9a1b66 100644 --- a/addons/gut/input_sender.gd +++ b/addons/gut/input_sender.gd @@ -73,7 +73,7 @@ class InputQueueItem: if(frame_delay > 0 and _delay_started): _waited_frames += 1 if(_waited_frames >= frame_delay): - emit_signal("event_ready") + event_ready.emit() func _init(t_delay,f_delay): time_delay = t_delay @@ -82,7 +82,7 @@ class InputQueueItem: func _on_time_timeout(): _is_ready = true - emit_signal("event_ready") + event_ready.emit() func _delay_timer(t): return Engine.get_main_loop().root.get_tree().create_timer(t) @@ -344,7 +344,7 @@ func _on_queue_item_ready(item): if(_input_queue.size() == 0): _next_queue_item = null - emit_signal("idle") + idle.emit() else: _input_queue[0].start() diff --git a/addons/gut/logger.gd b/addons/gut/logger.gd index 6e347ecb..b7e97694 100644 --- a/addons/gut/logger.gd +++ b/addons/gut/logger.gd @@ -55,7 +55,7 @@ var fmts = { } var _type_data = { - types.debug: {disp='DEBUG', enabled=true, fmt=fmts.none}, + types.debug: {disp='DEBUG', enabled=true, fmt=fmts.bold}, types.deprecated: {disp='DEPRECATED', enabled=true, fmt=fmts.none}, types.error: {disp='ERROR', enabled=true, fmt=fmts.red}, types.failed: {disp='Failed', enabled=true, fmt=fmts.red}, @@ -142,8 +142,7 @@ func _print_test_name(): func _output(text, fmt=null): for key in _printers: if(_should_print_to_printer(key)): - var info = ''#str(self, ':', key, ':', _printers[key], '| ') - _printers[key].send(info + text, fmt) + _printers[key].send(text, fmt) func _log(text, fmt=fmts.none): _print_test_name() diff --git a/addons/gut/plugin.cfg b/addons/gut/plugin.cfg index 510f4046..d6ce8f3d 100644 --- a/addons/gut/plugin.cfg +++ b/addons/gut/plugin.cfg @@ -3,5 +3,5 @@ name="Gut" description="Unit Testing tool for Godot." author="Butch Wesley" -version="9.2.1" +version="9.3.0" script="gut_plugin.gd" diff --git a/addons/gut/script_parser.gd b/addons/gut/script_parser.gd index 10d18cc3..ff5a556e 100644 --- a/addons/gut/script_parser.gd +++ b/addons/gut/script_parser.gd @@ -91,16 +91,14 @@ class ParsedScript: get: return _resource set(val): return; - var _native_instance = null - var is_native = false : - get: return _native_instance != null + var _is_native = false + var is_native = _is_native: + get: return _is_native set(val): return; - func _notification(what, reversed=false): - if(what == NOTIFICATION_PREDELETE): - if(_native_instance != null): - _native_instance.free() + var _native_methods = {} + var _native_class_name = "" @@ -109,7 +107,11 @@ class ParsedScript: if(GutUtils.is_native_class(to_load)): _resource = to_load - _native_instance = to_load.new() + _is_native = true + var inst = to_load.new() + _native_class_name = inst.get_class() + _native_methods = inst.get_method_list() + inst.free() else: if(!script_or_inst is Resource): to_load = load(script_or_inst.get_script().get_path()) @@ -145,7 +147,7 @@ class ParsedScript: func _parse_methods(thing): var methods = [] if(is_native): - methods = _native_instance.get_method_list() + methods = _native_methods.duplicate() else: var base_type = thing.get_instance_base_type() methods = _get_native_methods(base_type) @@ -259,7 +261,7 @@ class ParsedScript: func get_extends_text(): var text = null if(is_native): - text = str("extends ", _native_instance.get_class()) + text = str("extends ", _native_class_name) else: text = str("extends '", _script_path, "'") if(_subpath != null): diff --git a/addons/gut/stub_params.gd b/addons/gut/stub_params.gd index 7806a51e..d24d90d7 100644 --- a/addons/gut/stub_params.gd +++ b/addons/gut/stub_params.gd @@ -141,6 +141,9 @@ func to_s(): if(call_super): base_string += " to call SUPER" + if(call_this != null): + base_string += str(" to call ", call_this) + if(parameters != null): base_string += str(' with params (', parameters, ') returns ', return_val) else: diff --git a/addons/gut/test.gd b/addons/gut/test.gd index 4ec9e133..c1f34634 100644 --- a/addons/gut/test.gd +++ b/addons/gut/test.gd @@ -1567,7 +1567,7 @@ func run_x_times(x): "know why you're doing this.")) var params = [] for i in range(x): - params.append([]) + params.append(i) ph = GutUtils.ParameterHandler.new(params) gut.parameter_handler = ph diff --git a/addons/gut/test_collector.gd b/addons/gut/test_collector.gd index 308230a9..b0047585 100644 --- a/addons/gut/test_collector.gd +++ b/addons/gut/test_collector.gd @@ -110,7 +110,7 @@ func add_script(path): # them to be run through gut. This helps cut down on creating test # scripts to be used in test/resources. if(ResourceLoader.has_cached(path)): - _lgr.info("Using cached version of " + path) + _lgr.debug("Using cached version of " + path) else: _lgr.error('Could not find script: ' + path) return diff --git a/addons/gut/utils.gd b/addons/gut/utils.gd index 5aaef31d..6580670e 100644 --- a/addons/gut/utils.gd +++ b/addons/gut/utils.gd @@ -149,7 +149,7 @@ static var avail_fonts = ['AnonymousPro', 'CourierPrime', 'LobsterTwo', 'Default static var version_numbers = VersionNumbers.new( # gut_versrion (source of truth) - '9.2.1', + '9.3.0', # required_godot_version '4.2.0' ) diff --git a/documentation/docs/Input-Sender.md b/documentation/docs/Input-Sender.md index 22220aed..0044b144 100644 --- a/documentation/docs/Input-Sender.md +++ b/documentation/docs/Input-Sender.md @@ -6,6 +6,8 @@ The `InputSender` class can be used to send `InputEvent*` events to various obje And much much more. +__Warning__
+If you move the Godot window to a different monitor while tests are running it can cause input tests to fail. [This issue](https://github.com/bitwes/Gut/issues/643) has more details. ## Signals * `idle` - Emitted when all events in the input queue have been sent. diff --git a/documentation/docs/conf.py b/documentation/docs/conf.py index 1ec8f765..32a0231a 100644 --- a/documentation/docs/conf.py +++ b/documentation/docs/conf.py @@ -6,8 +6,8 @@ copyright = '2024, Butch Wesley' author = 'bitwes' -release = '9.2.1' -version = '9.2.1 for Godot-4' +release = '9.3.0' +version = '9.3.0 for Godot-4' # -- General configuration diff --git a/documentation/docs/index.rst b/documentation/docs/index.rst index c5418863..c268f27f 100644 --- a/documentation/docs/index.rst +++ b/documentation/docs/index.rst @@ -66,7 +66,7 @@ -Gut 9.2.1 (Godot 4.2) +Gut 9.3.0 (Godot 4.2) ========= GUT (Godot Unit Test) is a utility for writing tests for your Godot Engine game. It allows you to write tests for your gdscripts in gdscript. diff --git a/test/gut_test.gd b/test/gut_test.gd index 378386a0..e45133f2 100644 --- a/test/gut_test.gd +++ b/test/gut_test.gd @@ -1,5 +1,6 @@ class_name GutInternalTester extends GutTest + var verbose = false const DOUBLE_ME_PATH = 'res://test/resources/doubler_test_objects/double_me.gd' @@ -123,7 +124,7 @@ func assert_fail_msg_contains(t, text): func get_error_count(obj): return obj.logger.get_errors().size() - +var new_gut_indent_string = "| " func new_gut(print_sub_tests=false): var g = Gut.new() g.logger = Logger.new() @@ -134,8 +135,9 @@ func new_gut(print_sub_tests=false): g.logger.disable_printer("terminal", false) g.logger._min_indent_level = 1 g.logger.dec_indent() - g.logger.set_indent_string('|##| ') + g.logger.set_indent_string(new_gut_indent_string) g.logger.disable_formatting(!print_sub_tests) + g.logger.set_type_enabled(g.logger.types.debug, true) g._should_print_versions = false g._should_print_summary = false @@ -153,7 +155,7 @@ func new_partial_double_gut(print_sub_tests=false): g.logger.disable_printer("terminal", false) g.logger._min_indent_level = 1 g.logger.dec_indent() - g.logger.set_indent_string('|##| ') + g.logger.set_indent_string(new_gut_indent_string) g.logger.disable_formatting(!print_sub_tests) else: g.log_level = g.LOG_LEVEL_FAIL_ONLY @@ -164,9 +166,9 @@ func new_partial_double_gut(print_sub_tests=false): return g -func new_no_print_logger(): +func new_no_print_logger(override=!verbose): var to_return = Logger.new() - to_return.disable_all_printers(true) + to_return.disable_all_printers(override) return to_return @@ -186,4 +188,5 @@ func new_wired_test(gut_instance): # stub(t, 'get_logger').to_call_super() # t.set_logger(logger) # return t -# ---------------------------- \ No newline at end of file +# ---------------------------- + diff --git a/test/integration/test_gut_skip_scripts.gd b/test/integration/test_gut_skip_scripts.gd index 8f1a6f49..77449d14 100644 --- a/test/integration/test_gut_skip_scripts.gd +++ b/test/integration/test_gut_skip_scripts.gd @@ -1,126 +1,115 @@ extends GutInternalTester -var _src_base = """ -extends GutTest -""" var _src_passing_test = """ -func test_is_passing(): - assert_true(true) -""" -var _src_failing_test = """ -func test_is_failing(): - assert_eq(1, '1') -""" -var _src_skip_script_var_string_val = """ -var skip_script = 'skip me, thanks' -""" -var _src_skip_script_var_null_val = """ -var skip_script = null -""" + func test_is_passing(): + assert_true(true) + """ var _src_should_skip_script_method_ret_true = """ -func should_skip_script(): - return true -""" + func should_skip_script(): + return true + """ var _src_should_skip_script_method_ret_false = """ -func should_skip_script(): - return false -""" + func should_skip_script(): + return false + """ var _src_should_skip_script_method_ret_string = """ -func should_skip_script(): - return 'skip me' -""" + func should_skip_script(): + return 'skip me' + """ -func _run_test_source(src): - var g = new_gut(true) - add_child_autofree(g) +var _gut = null - var dyn = GutUtils.create_script_from_source(src) - g.get_test_collector().add_script(dyn.resource_path) - g.run_tests() +func before_all(): + verbose = false + DynamicGutTest.should_print_source = verbose - var s = GutUtils.Summary.new() - return s.get_totals(g) - - -func test_can_compose_and_run_a_script(): - var src = _src_base +\ - _src_passing_test +\ - _src_failing_test - var t = _run_test_source(src) - - assert_eq(t.tests, 2) +func before_each(): + _gut = add_child_autofree(new_gut(verbose)) +# -------------- +# skip var +# -------------- func test_using_skip_script_variable_is_deprecated(): - var src = _src_base + \ - _src_skip_script_var_string_val + \ - _src_passing_test - var t = _run_test_source(src) - + var s = DynamicGutTest.new() + s.add_source("var skip_script = 'skip me thanks'") + s.add_source(_src_passing_test) + var t = s.run_test_in_gut(_gut) assert_eq(t.deprecated, 1, 'Should be one deprecation.') func test_when_skip_script_var_is_string_script_is_skipped(): - var src = _src_base + \ - _src_skip_script_var_string_val + \ - _src_passing_test - var t = _run_test_source(src) - - assert_eq(t.tests, 0, 'no tests should be ran') - assert_eq(t.risky, 1, 'Should be marked as risky due to skip') + var s = DynamicGutTest.new() + s.add_source("var skip_script = 'skip me'") + s.add_source(_src_passing_test) + var smry = s.run_test_in_gut(_gut) + assert_eq(smry.tests, 0, 'no tests should be ran') + assert_eq(smry.risky, 1, 'Should be marked as risky due to skip') func test_when_skip_script_var_is_null_the_script_is_ran(): - var src = _src_base + \ - _src_skip_script_var_null_val + \ - _src_passing_test - var t = _run_test_source(src) + var s = DynamicGutTest.new() + s.add_source("var skip_script = null") + s.add_source(_src_passing_test) - assert_eq(t.tests, 1, 'the one test should be ran') - assert_eq(t.risky, 0, 'not marked risky just for having var') + var smry = s.run_test_in_gut(_gut) + assert_eq(smry.tests, 1, 'the one test should be ran') + assert_eq(smry.risky, 0, 'not marked risky just for having var') +func test_when_skip_scrpt_var_is_true_the_script_is_skipped(): + var s = DynamicGutTest.new() + s.add_source("var skip_script = true") + s.add_source(_src_passing_test) + var smry = s.run_test_in_gut(_gut) + + assert_eq(smry.tests, 0, 'no tests should be ran') + assert_eq(smry.risky, 1, 'Should be marked as risky due to skip') + + +# -------------- +# skip method +# -------------- func test_should_skip_script_method_returns_false_by_default(): var test = autofree(GutTest.new()) assert_false(test.should_skip_script()) func test_when_should_skip_script_returns_false_script_is_run(): - var src = _src_base + \ - _src_should_skip_script_method_ret_false + \ - _src_passing_test - var t = _run_test_source(src) - - assert_eq(t.tests, 1, 'no tests should be ran') - assert_eq(t.risky, 0, 'Should be marked as risky due to skip') + var s = DynamicGutTest.new() + s.add_source(_src_should_skip_script_method_ret_false) + s.add_source(_src_passing_test) + var smry = s.run_test_in_gut(_gut) + assert_eq(smry.tests, 1, 'Tests should run') + assert_eq(smry.risky, 0, 'Should not be risky') func test_when_should_skip_script_returns_true_script_is_skipped(): - var src = _src_base + \ - _src_should_skip_script_method_ret_true + \ - _src_passing_test - var t = _run_test_source(src) + var s = DynamicGutTest.new() + s.add_source(_src_should_skip_script_method_ret_true) + s.add_source(_src_passing_test) + var smry = s.run_test_in_gut(_gut) - assert_eq(t.tests, 0, 'no tests should be ran') - assert_eq(t.risky, 1, 'Should be marked as risky due to skip') + assert_eq(smry.tests, 0, 'no tests should be ran') + assert_eq(smry.risky, 1, 'Should be marked as risky due to skip') func test_when_should_skip_script_returns_string_script_is_skipped(): - var src = _src_base + \ - _src_should_skip_script_method_ret_string + \ - _src_passing_test - var t = _run_test_source(src) + var s = DynamicGutTest.new() + s.add_source(_src_should_skip_script_method_ret_string) + s.add_source(_src_passing_test) + var smry = s.run_test_in_gut(_gut) - assert_eq(t.tests, 0, 'no tests should be ran') - assert_eq(t.risky, 1, 'Should be marked as risky due to skip') + assert_eq(smry.tests, 0, 'no tests should be ran') + assert_eq(smry.risky, 1, 'Should be marked as risky due to skip') func test_using_should_skip_script_method_is_not_deprecated(): - var src = _src_base + \ - _src_should_skip_script_method_ret_true + \ - _src_passing_test - var t = _run_test_source(src) + var s = DynamicGutTest.new() + s.add_source(_src_should_skip_script_method_ret_true) + s.add_source(_src_passing_test) + var smry = s.run_test_in_gut(_gut) - assert_eq(t.deprecated, 0, 'nothing is deprecated') + assert_eq(smry.deprecated, 0, 'nothing is deprecated') diff --git a/test/resources/dynamic_gut_test.gd b/test/resources/dynamic_gut_test.gd new file mode 100644 index 00000000..43aa10e1 --- /dev/null +++ b/test/resources/dynamic_gut_test.gd @@ -0,0 +1,69 @@ +class_name DynamicGutTest +# ------------------------------------------------------------------------------ +# Used to create dynamic GutTest scripts for integration tests that require +# running tests through GUT. This makes it easier to keep the source of the +# test scripts created in tests to test the tests closer to the tests that +# test the test. +# ------------------------------------------------------------------------------ +static var should_print_source = true +var source_entries = [] +var lambdas = [] + + +func make_source(): + var src = "extends GutTest\n" + for e in source_entries: + src += str(e, "\n") + + return src + + +func make_script(): + return GutUtils.create_script_from_source(make_source()) + + +func make_new(): + var to_return = make_script().new() + if(should_print_source): + print(to_return.get_script().resource_path) + print_source() + + return to_return + + +func add_source(p1='', p2='', p3='', p4='', p5='', p6='', p7='', p8='', p9='', p10=''): + var source = str(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) + source_entries.append(source.dedent()) + return self + + +func add_lambda_test(lambda, test_name=null): + var idx = lambdas.size() + var func_name = test_name + if(func_name == null): + func_name = str("test_run_lambda_", idx) + lambdas.append(lambda) + add_source("func ", func_name, "():\n", + "\tinstance_from_id(", get_instance_id(), ").lambdas[", idx, "].call(self)") + return self + + +func add_as_test_to_gut(which): + var dyn = make_script() + if(should_print_source): + print(dyn.resource_path) + print_source() + + which.get_test_collector().add_script(dyn.resource_path) + + +func run_test_in_gut(which): + add_as_test_to_gut(which) + which.run_tests() + var s = GutUtils.Summary.new() + return s.get_totals(which) + + +func print_source(): + print(GutUtils.add_line_numbers(make_source())) + diff --git a/test/resources/wait_awhile.gd b/test/resources/wait_awhile.gd new file mode 100644 index 00000000..60d23e9d --- /dev/null +++ b/test/resources/wait_awhile.gd @@ -0,0 +1,13 @@ +extends GutTest +# ------------------------------------------------------------------------------ +# This is used to give you some time to do stuff before a test script is run. +# This is intended to be used with -gtest option from the command line (hence +# this file does not have the test_ prefix and it is in the resources dir). +# ------------------------------------------------------------------------------ +var seconds_to_wait = 10 + +func test_this_waits_for_a_bit(): + for i in range(seconds_to_wait): + gut.p(seconds_to_wait - i) + await wait_seconds(1) + pass_test("This passes because it just waits") \ No newline at end of file diff --git a/test/unit/test_bugs/test_i578.gd b/test/unit/test_bugs/test_i578.gd index 95f39353..754be558 100644 --- a/test/unit/test_bugs/test_i578.gd +++ b/test/unit/test_bugs/test_i578.gd @@ -8,7 +8,7 @@ class InputSingletonTracker: var _frame_counter = 0 - func _process(delta): + func _process(_delta): _frame_counter += 1 if(Input.is_action_just_pressed("jump")): @@ -20,16 +20,45 @@ class InputSingletonTracker: if Input.is_action_pressed("jump"): pressed_frames.append(_frame_counter) + + +# ------------------------------------------------------------------------------ +# There are a few tests in here that will fail if the window changes to +# a different monitor at any point before these tests are run. I was able to +# replicate the issue consistently. +# +# Fails when (MacOS) +# * Drag the window to a different monitor. +# * Use keystroke (better-snap-tools) to move window. +# * Window is dragged and "held" on another monitor. +# +# Passes when (MacOS) +# * The window is not moved. +# * Window is moved around on the same monitor. +# * The window is being "held" on the original monitor. +# * Window is moved back to original monitor before these tests are executed. +# This appears to be the case regardless of the number of times the window +# changes monitor. +# +# To test these scenarios I used the following to run a script that just delays +# for a bit, and then run this script. +# gdscript addons/gut/gut_cmdln.gd -gexit -gconfig= -gtest test/resources/wait_awhile.gd,test/unit/test_bugs/test_i578.gd +# ------------------------------------------------------------------------------ class TestInputSingleton: extends "res://addons/gut/test.gd" var _sender = InputSender.new(Input) func before_all(): + _sender.release_all() + _sender.clear() + await wait_frames(10) InputMap.add_action("jump") + func after_all(): InputMap.erase_action("jump") + func after_each(): _sender.release_all() # Wait for key release to be processed. Otherwise the key release is @@ -37,27 +66,28 @@ class TestInputSingleton: await wait_frames(1) _sender.clear() + func test_raw_input_press(): var r = add_child_autofree(InputSingletonTracker.new()) Input.action_press("jump") - await wait_frames(10) + await wait_frames(2) Input.action_release("jump") - assert_gt(r.pressed_frames.size(), 1, 'input size') - + # see inner-test-class note + assert_gt(r.pressed_frames.size(), 1, 'input size (FAILS if window changes monitor)') + func test_input_sender_press(): var r = add_child_autofree(InputSingletonTracker.new()) _sender.action_down("jump").hold_for('10f') await wait_for_signal(_sender.idle, 5) - print(r.pressed_frames.size()) assert_gt(r.pressed_frames.size(), 1, 'input size') func test_input_sender_just_pressed(): var r = add_child_autofree(InputSingletonTracker.new()) - + _sender.action_down("jump").hold_for("20f") await wait_frames(5) @@ -66,9 +96,10 @@ class TestInputSingleton: func test_input_sender_just_released(): var r = add_child_autofree(InputSingletonTracker.new()) - + _sender.action_down("jump").hold_for('5f') await wait_for_signal(_sender.idle, 10) assert_eq(r.just_pressed_count, 1, 'just pressed once') - assert_eq(r.just_released_count, 1, 'released key once') + # see inner-test-class note + assert_eq(r.just_released_count, 1, 'released key once (FAILS if window changes monitor)') diff --git a/test/unit/test_collected_script.gd b/test/unit/test_collected_script.gd index d41e01cf..77618048 100644 --- a/test/unit/test_collected_script.gd +++ b/test/unit/test_collected_script.gd @@ -6,40 +6,40 @@ var CollectedScript = GutUtils.CollectedScript func test_get_risky_count_counts_all_tests_that_were_run(): - var c_script = CollectedScript.new() + var c_script = CollectedScript.new() - for i in range(3): - var c_test = CollectedTest.new() - c_script.tests.append(c_test) - c_test.was_run = true + for i in range(3): + var c_test = CollectedTest.new() + c_script.tests.append(c_test) + c_test.was_run = true - assert_eq(c_script.get_risky_count(), 3) + assert_eq(c_script.get_risky_count(), 3) func test_get_risky_count_does_not_count_scripts_not_run(): - var c_script = CollectedScript.new() + var c_script = CollectedScript.new() - for i in range(3): - var c_test = CollectedTest.new() - c_script.tests.append(c_test) - c_test.was_run = false + for i in range(3): + var c_test = CollectedTest.new() + c_script.tests.append(c_test) + c_test.was_run = false - assert_eq(c_script.get_risky_count(), 0) + assert_eq(c_script.get_risky_count(), 0) func test_get_ran_test_count_only_returns_tests_that_were_run(): - var c_script = CollectedScript.new() + var c_script = CollectedScript.new() - var c_test = CollectedTest.new() - c_script.tests.append(c_test) - c_test.was_run = true + var c_test = CollectedTest.new() + c_script.tests.append(c_test) + c_test.was_run = true - c_test = CollectedTest.new() - c_script.tests.append(c_test) - c_test.was_run = true + c_test = CollectedTest.new() + c_script.tests.append(c_test) + c_test.was_run = true - c_test = CollectedTest.new() - c_script.tests.append(c_test) - c_test.was_run = false + c_test = CollectedTest.new() + c_script.tests.append(c_test) + c_test.was_run = false - assert_eq(c_script.get_ran_test_count(), 2) \ No newline at end of file + assert_eq(c_script.get_ran_test_count(), 2) \ No newline at end of file diff --git a/test/unit/test_input_sender.gd b/test/unit/test_input_sender.gd index c5661db9..67ee476f 100644 --- a/test/unit/test_input_sender.gd +++ b/test/unit/test_input_sender.gd @@ -66,7 +66,7 @@ class InputTracker: print(e) class TestTheBasics: - extends "res://addons/gut/test.gd" + extends GutInternalTester func before_all(): InputMap.add_action("jump") @@ -161,7 +161,7 @@ class TestTheBasics: func test_warns_when_key_down_for_a_pressed_key(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.key_down("S") sender.key_down("S") @@ -169,7 +169,7 @@ class TestTheBasics: func test_does_now_warn_for_key_up(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.key_down("S") sender.key_up("S") @@ -177,7 +177,7 @@ class TestTheBasics: func test_does_not_warn_for_key_echos(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.key_down("S") sender.key_echo() @@ -186,7 +186,7 @@ class TestTheBasics: func test_warns_when_action_down_for_a_pressed_action(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.action_down("jump") sender.action_down("jump") @@ -194,7 +194,7 @@ class TestTheBasics: func test_does_not_warn_for_action_up(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.action_down("jump") sender.action_up("jump") @@ -202,7 +202,7 @@ class TestTheBasics: func test_warns_when_mouse_down_for_a_pressed_mouse_button(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.mouse_right_button_down(Vector2(1,1)) sender.mouse_right_button_down(Vector2(1,1)) @@ -210,7 +210,7 @@ class TestTheBasics: func test_does_not_warn_for_mouse_up(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.mouse_right_button_down(Vector2(1,1)) sender.mouse_right_button_up(Vector2(1,1)) @@ -218,7 +218,7 @@ class TestTheBasics: func test_does_not_warn_when_mouse_button_released(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.mouse_right_button_down(Vector2(1,1)) sender.mouse_right_button_up(Vector2(1,1)) @@ -227,7 +227,7 @@ class TestTheBasics: func test_warns_for_2nd_down_event_after_idle(): var sender = InputSender.new() - var lgr = GutUtils.Logger.new() + var lgr = new_no_print_logger() sender._lgr = lgr sender.key_down("R").wait(.2) diff --git a/test/unit/test_optparse.gd b/test/unit/test_optparse.gd index a370dc34..62704f88 100644 --- a/test/unit/test_optparse.gd +++ b/test/unit/test_optparse.gd @@ -45,6 +45,11 @@ class TestOption: var o = OptParse.Option.new('name', 'default') assert_false(o.required) + func test_setting_value_to_an_array_makes_has_been_set_true(): + var o = OptParse.Option.new("name", []) + o.value = [1, 2, 3] + assert_true(o.has_been_set()) + class TestOptParse: extends BaseTest @@ -281,6 +286,13 @@ class TestArrayParameters: op.parse(['--foo=a,b', '--foo', 'c,d', '--foo', 'e']) assert_eq(option.value, ['a', 'b', 'c', 'd', 'e']) + func test_after_parsing_value_has_been_set_is_true(): + var op = OptParse.new() + var option = op.add('--foo', [], 'foo array') + op.parse(['--foo=a,b']) + assert_eq(option.value, ['a', 'b']) + assert_true(option.has_been_set()) + diff --git a/test/unit/test_script_parser.gd b/test/unit/test_script_parser.gd index cfc13d9b..ea287943 100644 --- a/test/unit/test_script_parser.gd +++ b/test/unit/test_script_parser.gd @@ -9,8 +9,6 @@ class TestScriptParser: var ExtendsNode = load('res://test/resources/doubler_test_objects/double_extends_node2d.gd') const INNER_CLASSES_PATH = 'res://test/resources/doubler_test_objects/inner_classes.gd' var InnerClasses = load(INNER_CLASSES_PATH) - - var ScriptParser = load('res://addons/gut/script_parser.gd') func test_can_make_one(): @@ -72,6 +70,8 @@ class TestScriptParser: assert_eq(parsed.script_path, INNER_CLASSES_PATH) + + class HasAccessors: var my_property = 'default' : get: return my_property @@ -209,6 +209,9 @@ class TestParsedScript: var method = parsed.get_method('@my_property_setter') assert_true(method.is_accessor()) + + + class TestParsedMethod: extends GutTest diff --git a/test/unit/test_test.gd b/test/unit/test_test.gd index 9089aedf..ec494ae2 100644 --- a/test/unit/test_test.gd +++ b/test/unit/test_test.gd @@ -1829,17 +1829,35 @@ class TestParameterizedTests: # ------------------------------------------------------------------------------ class TestMemoryMgmt: - extends GutTest + extends GutInternalTester + + var _gut = null + + func before_each(): + # verbose = true + _gut = add_child_autofree(new_gut(verbose)) func test_passes_when_no_orphans_introduced(): - assert_no_new_orphans() - assert_true(is_passing(), 'test should be passing') + var d = DynamicGutTest.new() + d.add_source(""" + func test_assert_no_orphans(): + assert_no_new_orphans() + """) + var results = d.run_test_in_gut(_gut) + assert_eq(results.passing, 1) func test_failing_orphan_assert_marks_test_as_failing(): - var n2d = Node2D.new() - assert_no_new_orphans('SHOULD FAIL') - assert_true(is_failing(), 'this test should be failing') - n2d.free() + var d = DynamicGutTest.new() + d.add_source(""" + func test_assert_no_orphans(): + var n2d = Node2D.new() + assert_no_new_orphans('SHOULD FAIL') + assert_true(is_failing(), 'this test should be failing') + n2d.free() + """) + var results = d.run_test_in_gut(_gut) + assert_eq(results.failing, 1) + func test_passes_when_orphans_released(): var n2d = Node2D.new()