Files
game-cards/addons/gut/test/unit/test_gut.gd
2026-05-29 09:16:10 +08:00

976 lines
34 KiB
GDScript

# ------------------------------------------------------------------------------
# Test the Gut object.
# ------------------------------------------------------------------------------
extends GutTest
class TestProperties:
extends GutInternalTester
var _gut = null
func before_each():
# Can't use new_gut here because of default value checks
_gut = autofree(Gut.new(GutUtils.GutLogger.new()))
_gut.error_tracker = GutErrorTracker.new()
_gut._should_print_versions = false
var _backed_properties = ParameterFactory.named_parameters(
['property_name', 'default', 'new_value'],
[
['color_output', false, true],
['disable_strict_datatype_checks', false, true],
['disable_strict_datatype_checks', false, true],
['double_strategy', GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY, GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE],
['export_path', '', 'res://somewhere/cool'],
['ignore_pause_before_teardown', false, true],
['include_subdirectories', false, true],
['inner_class_name', '', 'TestSomeInnerClass'],
['junit_xml_file', '', 'user://somewhere.json'],
['junit_xml_timestamp', false, true],
['log_level', 1, 3],
['parameter_handler', null, GutUtils.ParameterHandler.new([])],
['post_run_script', '', 'res://something_else.gd'],
['pre_run_script', '', 'res://something.gd'],
['unit_test_name', '', 'test_something_cool'],
])
func test_check_backed_properties(p=use_parameters(_backed_properties)):
assert_property_with_backing_variable(_gut, p.property_name, p.default, p.new_value)
var _basic_properties = ParameterFactory.named_parameters(
['property_name', 'default', 'new_value'],
[
['paint_after', .1, 1.5]
])
func test_basic_properties(p = use_parameters(_basic_properties)):
assert_property(_gut, p.property_name, p.default, p.new_value)
# This must be its own test since _gut will not be anything at
# the time _backed_properties is assigned
func test_property_add_children_to_backed():
assert_property_with_backing_variable(_gut, 'add_children_to', _gut, self)
func test_logger_backed_property():
assert_property_with_backing_variable(_gut, 'logger', _gut._lgr, GutUtils.GutLogger.new(), '_lgr')
func test_setting_logger_sets_gut_for_logger():
var new_logger = GutUtils.GutLogger.new()
_gut.logger = new_logger
assert_eq(new_logger.get_gut(), _gut)
func test_get_current_script_object_returns_null_by_default():
assert_eq(_gut.get_current_script_object(), null)
# I don't know how to test this in other situations
func test_set_double_strategy_does_not_accept_invalid_values():
var default = _gut.double_strategy
_gut.double_strategy = -1
assert_eq(_gut.double_strategy, default, 'did not accept -1')
_gut.double_strategy = 22
assert_eq(_gut.double_strategy, default, 'did not accept 22')
class TestSimulate:
extends GutInternalTester
var _test_gut = null
func before_each():
_test_gut = autofree(new_gut(verbose))
class WithoutProcess:
extends Node
class WithProcess:
extends Node
var call_count = 0
var delta_sum = 0.0
func _process(delta):
call_count += 1
delta_sum += delta
class WithoutPhysicsProcess:
extends Node
class WithPhysicsProcess:
extends Node
var call_count = 0
var delta_sum = 0.0
func _physics_process(delta):
call_count += 1
delta_sum += delta
func test_simulate_calls_process_if_object_has_method():
var with_method = autofree(WithProcess.new())
_test_gut.simulate(with_method, 5, 0.2)
assert_eq(with_method.call_count, 5, '_process should have been called 5 times')
assert_eq(with_method.delta_sum, 1.0, 'The delta value should have been passed in and summed')
func test_simulate_does_not_error_when_object_does_not_have_process():
var without_method = autofree(WithoutProcess.new())
_test_gut.simulate(without_method, 5, 0.2)
pass_test('We got here')
func test_simulate_calls_process_on_child_objects_of_child_objects():
var objs = []
for i in range(5):
objs.append(autofree(WithProcess.new()))
if(i > 0):
objs[i - 1].add_child(objs[i])
_test_gut.simulate(objs[0], 5, 0.2)
for i in range(objs.size()):
assert_eq(objs[i].call_count, 5, '_process should have been called on object # ' + str(i))
assert_eq(objs[i].delta_sum, 1, 'The delta value should have been summed on object # ' + str(i))
func test_simulate_checks_process_on_all_nodes():
var objs = [
autofree(WithProcess.new()),
autofree(WithoutProcess.new()),
autofree(WithProcess.new()),
autofree(WithoutProcess.new()),
]
for i in range(1, 4):
objs[i - 1].add_child(objs[i])
_test_gut.simulate(objs[0], 5, 0.2)
assert_eq(objs[0].call_count, 5, '_process should have been called 5 times')
assert_eq(objs[0].delta_sum, 1.0, 'The delta value should have been passed in and summed')
assert_eq(objs[2].call_count, 5, '_process should have been called 5 times')
assert_eq(objs[2].delta_sum, 1.0, 'The delta value should have been passed in and summed')
func test_simulate_calls_process_if_object_is_processing_and_check_is_true():
var with_processing = autofree(WithProcess.new())
with_processing.set_process(true)
_test_gut.simulate(with_processing, 5, 0.2, true) # check_is_processing=false
assert_eq(with_processing.call_count, 5, '_process should have been called 5 times')
assert_eq(with_processing.delta_sum, 1.0, 'The delta value should have been passed in and summed')
func test_simulate_does_not_call_process_if_object_is_not_processing_and_check_is_true():
var without_processing = autofree(WithProcess.new())
without_processing.set_process(false)
_test_gut.simulate(without_processing, 5, 0.2, true) # check_is_processing=true
assert_eq(without_processing.call_count, 0, '_process should not have been called')
func test_simulate_does_not_error_if_object_is_processing_but_has_no_method():
var with_processing_but_without_method = autofree(WithoutProcess.new())
with_processing_but_without_method.set_process(true)
_test_gut.simulate(with_processing_but_without_method, 5, 0.2, true) # check_is_processing=true
pass_test('We got here')
func test_simulate_calls_process_on_descendents_if_objects_are_processing():
var objs = [
autofree(WithProcess.new()),
autofree(WithoutProcess.new()),
autofree(WithProcess.new()),
autofree(WithoutProcess.new()),
]
for i in range(1, 4):
objs[i - 1].add_child(objs[i])
objs[0].set_process(false)
objs[1].set_process(false)
objs[2].set_process(true)
objs[3].set_process(true)
_test_gut.simulate(objs[0], 5, 0.2, true) # check_is_processing=true
assert_eq(objs[0].call_count, 0, '_process should not have been called')
assert_eq(objs[2].call_count, 5, '_process should have been called 5 times')
assert_eq(objs[2].delta_sum, 1.0, 'The delta value should have been passed in and summed')
func test_simulate_calls_physics_process_if_object_has_method():
var with_method = autofree(WithPhysicsProcess.new())
_test_gut.simulate(with_method, 5, 0.2)
assert_eq(with_method.call_count, 5, '_physics_process should have been called 5 times')
assert_eq(with_method.delta_sum, 1.0, 'The delta value should have been passed in and summed')
func test_simulate_does_not_error_when_object_does_not_have_physics_process():
var without_method = autofree(WithoutPhysicsProcess.new())
_test_gut.simulate(without_method, 5, 0.2)
pass_test('We got here')
func test_simulate_calls_physics_process_on_child_objects_of_child_objects():
var objs = []
for i in range(5):
objs.append(autofree(WithPhysicsProcess.new()))
if(i > 0):
objs[i - 1].add_child(objs[i])
_test_gut.simulate(objs[0], 5, 0.2)
for i in range(objs.size()):
assert_eq(objs[i].call_count, 5, '_physics_process should have been called on object # ' + str(i))
assert_eq(objs[i].delta_sum, 1, 'The delta value should have been summed on object # ' + str(i))
func test_simulate_calls_physics_process_on_descendents_if_objects_have_method():
var objs = [
autofree(WithPhysicsProcess.new()),
autofree(WithoutPhysicsProcess.new()),
autofree(WithPhysicsProcess.new()),
autofree(WithoutPhysicsProcess.new()),
]
for i in range(1, 4):
objs[i - 1].add_child(objs[i])
_test_gut.simulate(objs[0], 5, 0.2)
assert_eq(objs[0].call_count, 5, '_physics_process should have been called 5 times')
assert_eq(objs[0].delta_sum, 1.0, 'The delta value should have been passed in and summed')
assert_eq(objs[2].call_count, 5, '_physics_process should have been called 5 times')
assert_eq(objs[2].delta_sum, 1.0, 'The delta value should have been passed in and summed')
func test_simulate_calls_physics_process_if_object_is_processing_and_check_is_true():
var with_processing = autofree(WithPhysicsProcess.new())
with_processing.set_physics_process(true)
_test_gut.simulate(with_processing, 5, 0.2, true) # check_is_processing=false
assert_eq(with_processing.call_count, 5, '_physics_process should have been called 5 times')
assert_eq(with_processing.delta_sum, 1.0, 'The delta value should have been passed in and summed')
func test_simulate_does_not_call_physics_process_if_object_is_not_processing_and_check_is_true():
var without_processing = autofree(WithPhysicsProcess.new())
without_processing.set_physics_process(false)
_test_gut.simulate(without_processing, 5, 0.2, true) # check_is_processing=true
assert_eq(without_processing.call_count, 0, '_physics_process should not have been called')
func test_simulate_does_not_error_if_object_is_physics_processing_but_has_no_method():
var with_processing_but_without_method = autofree(WithoutPhysicsProcess.new())
with_processing_but_without_method.set_physics_process(true)
_test_gut.simulate(with_processing_but_without_method, 5, 0.2, true) # check_is_processing=true
pass_test('We got here')
func test_simulate_calls_physics_process_on_descendents_if_objects_are_processing():
var objs = [
autofree(WithPhysicsProcess.new()),
autofree(WithoutPhysicsProcess.new()),
autofree(WithPhysicsProcess.new()),
autofree(WithoutPhysicsProcess.new()),
]
for i in range(1, 4):
objs[i - 1].add_child(objs[i])
objs[0].set_physics_process(false)
objs[1].set_physics_process(false)
objs[2].set_physics_process(true)
objs[3].set_physics_process(true)
_test_gut.simulate(objs[0], 5, 0.2, true) # check_is_processing=true
assert_eq(objs[0].call_count, 0, '_physics_process should not have been called')
assert_eq(objs[2].call_count, 5, '_physics_process should have been called 5 times')
assert_eq(objs[2].delta_sum, 1.0, 'The delta value should have been passed in and summed')
class TestMisc:
extends GutInternalTester
func test_gut_does_not_make_orphans_when_added_to_scene():
var g = new_gut()
add_child(g)
g.free()
assert_no_new_orphans()
func test_gut_does_not_make_orphans_when_freed_before_in_tree():
var g = new_gut()
g.free()
await wait_physics_frames(2)
assert_no_new_orphans()
class TestEverythingElse:
extends GutInternalTester
#------------------------------
# Utility methods/variables
#------------------------------
# When these tests are ran in the context of other tests then the setup and
# teardown counts can get out of whack which causes the last test in here
# to fail. These counts are used to adjust the values tested against.
var starting_counts = {
setup_count = 0,
teardown_count = 0
}
var counts = {
setup_count = 0,
teardown_count = 0,
prerun_setup_count = 0,
postrun_teardown_count = 0
}
# GlobalReset(gr) variables to be used by tests.
# The values of these are reset in the setup or
# teardown methods.
var gr = {
test_gut = null,
test_finished_called = false,
signal_object = null,
test = null
}
func callback_for_test_finished():
gr.test_finished_called = true
# Returns a new gut object, all setup for testing.
func get_a_gut():
var g = autofree(new_gut(verbose))
return g
func run_tests(which = gr.test_gut):
which.test_scripts()
var signal_fired = await wait_for_signal(which.end_run, 2)
if(signal_fired):
await wait_seconds(.1)
# ------------------------------
# Setup/Teardown
# ------------------------------
func before_all():
starting_counts.setup_count = gut.get_test_count()
starting_counts.teardown_count = gut.get_test_count()
counts.prerun_setup_count += 1
func before_each():
counts.setup_count += 1
gr.test_finished_called = false
gr.test_gut = get_a_gut()
add_child_autoqfree(gr.test_gut)
gr.test = autofree(Test.new())
gr.test.gut = gr.test_gut
func after_each():
counts.teardown_count += 1
func after_all():
counts.postrun_teardown_count += 1
# can't verify that this ran, so do an assert.
# Asserts in any of the setup/teardown methods
# is a bad idea in general.
assert_true(true, 'POSTTEARDOWN RAN')
gut.directory_delete_files('user://')
# ------------------------------
# Doubler
# ------------------------------
func test_when_test_overrides_strategy_it_is_reset_after_test_finishes():
gr.test_gut.double_strategy = GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY
gr.test_gut.add_script('res://test/samples/test_before_after.gd')
gr.test_gut.get_doubler().set_strategy(GutUtils.DOUBLE_STRATEGY.INCLUDE_NATIVE)
await run_tests()
assert_eq(gr.test_gut.double_strategy, GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY)
func test_clears_ignored_methods_between_tests():
gr.test_gut.get_doubler().add_ignored_method('ignore_script', 'ignore_method')
gr.test_gut.add_script('res://test/samples/test_sample_one.gd')
gr.test_gut.unit_test_name = 'test_assert_eq_number_not_equal'
await run_tests()
assert_eq(gr.test_gut.get_doubler().get_ignored_methods().size(), 0)
pause_before_teardown()
# ------------------------------
# disable strict datatype comparisons
# ------------------------------
func test_when_strict_enabled_you_can_compare_int_and_float():
gr.test.assert_eq(1.0, 1)
assert_pass(gr.test)
func test_when_strict_disabled_can_compare_int_and_float():
gr.test_gut.disable_strict_datatype_checks = true
gr.test.assert_eq(1.0, 1)
assert_pass(gr.test)
# ------------------------------
# File utilities
# ------------------------------
func test_file_touch_creates_file():
var path = 'user://gut_test_touch.txt'
gut.file_touch(path)
gr.test.assert_file_exists(path)
assert_pass(gr.test)
func test_file_delete_kills_file():
var path = 'user://gut_test_file_delete.txt'
gr.test_gut.file_touch(path)
gr.test_gut.file_delete(path)
gr.test.assert_file_does_not_exist(path)
assert_pass(gr.test)
func test_delete_all_files_in_a_directory():
var path = 'user://gut_dir_tests'
var d = DirAccess.open('user://')
if(d != null):
d.make_dir('gut_dir_tests')
gr.test_gut.file_touch(path + '/helloworld.txt')
gr.test_gut.file_touch(path + '/file2.txt')
gr.test_gut.directory_delete_files(path)
gr.test.assert_file_does_not_exist(path + '/helloworld.txt')
gr.test.assert_file_does_not_exist(path + '/file2.txt')
gut.directory_delete_files('user://gut_dir_tests')
gut.file_delete('user://gut_dir_tests')
assert_pass(gr.test, 2, 'both files should not exist')
# ------------------------------
# No Assert Warning
# ------------------------------
# no assert was moved to risky, so this test changed to make sure the warning
# was no longer generated.
func test_when_a_test_has_no_asserts_risky_count_and_no_warning():
gr.test_gut.add_script('res://test/resources/per_test_assert_tracking.gd')
gr.test_gut.unit_test_name = 'test_no_asserts'
await run_tests()
assert_logger_warn(gr.test_gut, 0)
var risky_count = gr.test_gut.get_test_collector().scripts[0].get_risky_count()
assert_eq(risky_count, 1, 'Risky count')
func test_with_passing_assert_no_assert_warning_is_not_generated():
gr.test_gut.add_script('res://test/resources/per_test_assert_tracking.gd')
gr.test_gut.unit_test_name = 'test_passing_assert'
await run_tests()
assert_logger_warn(gr.test_gut, 0)
func test_with_failing_assert_no_assert_warning_is_not_generated():
gr.test_gut.add_script('res://test/resources/per_test_assert_tracking.gd')
gr.test_gut.unit_test_name = 'test_failing_assert'
await run_tests()
assert_logger_warn(gr.test_gut, 0)
func test_with_pass_test_call_no_assert_warning_is_not_generated():
gr.test_gut.add_script('res://test/resources/per_test_assert_tracking.gd')
gr.test_gut.unit_test_name = 'test_use_pass_test'
await run_tests()
assert_logger_warn(gr.test_gut, 0)
func test_with_fail_test_call_no_assert_warning_is_not_generated():
gr.test_gut.add_script('res://test/resources/per_test_assert_tracking.gd')
gr.test_gut.unit_test_name = 'test_use_fail_test'
await run_tests()
assert_logger_warn(gr.test_gut, 0)
func test_with_pending_call_no_assert_warning_is_no_generated():
gr.test_gut.add_script('res://test/resources/per_test_assert_tracking.gd')
gr.test_gut.unit_test_name = 'test_use_pending'
await run_tests()
assert_logger_warn(gr.test_gut, 0)
# ------------------------------
# Setting test to run
# ------------------------------
const SAMPLES_DIR = 'res://test/samples/'
func test_setting_name_will_run_only_matching_tests():
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
gr.test_gut.unit_test_name = 'test_works'
await run_tests()
assert_eq(gr.test_gut.get_test_count(), 1)
func test_setting_name_matches_partial():
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
gr.test_gut.unit_test_name = 'two'
await run_tests()
assert_eq(gr.test_gut.get_test_count(), 1)
# These should all pass, just making sure there aren't any syntax errors.
func test_asserts_on_test_object():
assert_eq(1, 1, 'text')
assert_ne(1, 2, 'text')
assert_almost_eq(5, 5, 0, 'text')
assert_almost_ne(5, 6, 0, 'text')
assert_gt(10, 5, 'text')
assert_lt(1, 2, 'text')
assert_true(true, 'text')
assert_false(false, 'text')
assert_between(5, 1, 10, 'text')
assert_file_does_not_exist('res://doesnotexist')
var path = 'user://gut_test_file.txt'
var f = FileAccess.open(path, FileAccess.WRITE)
f = null
assert_file_exists(path)
path = 'user://gut_test_empty.txt'
f = FileAccess.open(path, FileAccess.WRITE)
assert_file_empty(path)
f = null
path = 'user://gut_test_not_empty.txt'
f = FileAccess.open(path, FileAccess.WRITE)
f.store_8(100)
f.flush()
f = null
assert_file_not_empty(path)
func test_gut_clears_test_instances_between_runs():
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
gr.test_gut.test_scripts()
await run_tests()
assert_eq(gr.test_gut._test_script_objects.size(), 1, 'The should only be one test script after a second run')
# There might not be an easy way to free this orphan.
# ------------------------------
# Signal tests
# ------------------------------
func test_when_moving_to_next_test_watched_signals_are_cleared():
gr.test_gut.add_script('res://test/unit/verify_signal_watches_are_cleared.gd')
await run_tests()
assert_eq(gr.test_gut.get_pass_count(), 1, 'One test should have passed.')
assert_eq(gr.test_gut.get_fail_count(), 1, 'One failure for not watching anymore.')
assert_eq(gr.test_gut.get_test_count(), 2, 'should have ran two tests')
# ------------------------------
# Inner Class
# ------------------------------
func test_when_set_only_inner_class_tests_run():
gr.test_gut.inner_class_name = 'TestClass1'
gr.test_gut.add_script('res://test/resources/parsing_and_loading_samples/has_inner_class.gd')
await run_tests()
# count should be 4, 2 from TestClass1 and 2 from TestExtendsTestClass1
# which extends TestClass1 so it gets its two tests as well.
assert_eq(gr.test_gut.get_summary().get_totals().tests, 4)
func test_when_script_has_const_that_starts_with_Test_it_ignores_it():
gr.test_gut.add_script('res://test/resources/parsing_and_loading_samples/const_object.gd')
pass_test('we got here')
# ------------------------------
# Setup/before and teardown/after
# ------------------------------
func test_after_running_script_everything_checks_out():
gr.test_gut.add_script('res://test/samples/test_before_after.gd')
gr.test_gut.run_tests()
var instance = gr.test_gut.get_current_script_object()
assert_eq(instance.counts.before_all, 1, 'before_all')
assert_eq(instance.counts.before_each, 3, 'before_each')
assert_eq(instance.counts.after_all, 1, 'after_all')
assert_eq(instance.counts.after_each, 3, 'after_each')
await wait_seconds(1)
func test_when_inner_class_skipped_none_of_the_before_after_are_called():
gr.test_gut.add_script('res://test/resources/parsing_and_loading_samples/inner_classes_check_before_after.gd')
gr.test_gut.inner_class_name = 'Inner1'
gr.test_gut.run_tests()
var instances = gr.test_gut._test_script_objects
var inner1_inst = null
var inner2_inst = null
# order in which the inner classes will be run is unknown so we
# have to go looking for them.
for i in range(instances.size()):
var dict = inst_to_dict(instances[i])
var subpath = str(dict['@subpath'])
if(subpath == 'TestInner1'):
inner1_inst = instances[i]
elif(subpath == 'TestInner2'):
inner2_inst = instances[i]
assert_eq(inner1_inst.before_all_calls, 1, 'TestInner1 before_all calls')
assert_eq(inner1_inst.after_all_calls, 1, 'TestInner1 after_all calls')
assert_eq(inner1_inst.before_each_calls, 1, 'TestInner1 before_each_calls')
assert_eq(inner1_inst.after_each_calls, 1, 'TestInner1 after_each calls')
assert_eq(inner2_inst.before_all_calls, 0, 'TestInner2 before_all calls')
assert_eq(inner2_inst.after_all_calls, 0, 'TestInner2 after_all calls')
assert_eq(inner2_inst.before_each_calls, 0, 'TestInner2 before_each_calls')
assert_eq(inner2_inst.after_each_calls, 0, 'TestInner2 after_each calls')
await wait_seconds(1)
if(is_failing()):
gut.p('These sometimes fail due to the order tests are run.')
# ------------------------------
# Pre and post hook tests
# ------------------------------
func test_when_pre_hook_set_script_instance_is_is_retrievable():
var PreRunScript = load('res://test/resources/pre_run_script.gd')
gr.test_gut.pre_run_script = 'res://test/resources/pre_run_script.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
assert_is(gr.test_gut.get_pre_run_script_instance(), PreRunScript)
func test_when_pre_hook_set_run_method_is_called():
var PreRunScript = load('res://test/resources/pre_run_script.gd')
gr.test_gut.pre_run_script = 'res://test/resources/pre_run_script.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
assert_true(gr.test_gut.get_pre_run_script_instance().run_called)
func test_when_pre_hook_set_to_invalid_script_no_tests_are_ran():
gr.test_gut.pre_run_script = 'res://does_not_exist.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
assert_eq(gr.test_gut.get_test_count(), 0, 'test should not be run')
assert_tracked_gut_error(gr.test_gut, 2)
func test_pre_hook_sets_gut_instance():
gr.test_gut.pre_run_script = 'res://test/resources/pre_run_script.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
assert_eq(gr.test_gut.get_pre_run_script_instance().gut, gr.test_gut)
func test_pre_hook_accesses_global_lifecycle_signals():
gr.test_gut.pre_run_script = 'res://test/resources/pre_run_script_lifecycle_hooks.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
var retrieved_data = gr.test_gut.get_meta('pre_run_script_lifecycle_hooks_data')
assert_eq(retrieved_data[0], "test run started")
assert_eq(retrieved_data[1], "res://test/samples/test_sample_all_passed.gd loaded from collected script")
assert_eq(retrieved_data[2], "starting test test_works")
assert_eq(retrieved_data[3], "test_works passed")
assert_eq(retrieved_data[4], "starting test test_two")
assert_eq(retrieved_data[5], "test_two passed")
assert_eq(retrieved_data[6], "starting test test_3")
assert_eq(retrieved_data[7], "test_3 passed")
assert_eq(retrieved_data[8], "res://test/samples/test_sample_all_passed.gd passed")
assert_eq(retrieved_data[9], "test run ended")
func test_pre_hook_does_not_accept_non_hook_scripts():
gr.test_gut.pre_run_script = 'res://test/resources/non_hook_script.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
assert_eq(gr.test_gut.get_test_count(), 0, 'test should not be run')
assert_tracked_gut_error(gr.test_gut, 2)
func test_post_hook_is_run_after_tests():
var PostRunScript = load('res://test/resources/post_run_script.gd')
gr.test_gut.post_run_script = 'res://test/resources/post_run_script.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
await wait_seconds(1)
assert_is(gr.test_gut._post_run_script_instance, PostRunScript, 'Instance is set')
assert_true(gr.test_gut._post_run_script_instance.run_called, 'run was called')
func test_when_post_hook_set_to_invalid_script_no_tests_are_ran():
watch_signals(gr.test_gut)
gr.test_gut.post_run_script = 'res://does_not_exist.gd'
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
await run_tests()
assert_eq(gr.test_gut.get_test_count(), 0, 'test should not be run')
assert_tracked_gut_error(gr.test_gut, 2)
func test_awaiting_in_the_pre_hook_script():
var pre_run_script = load("res://test/resources/awaiting_pre_run_script.gd")
gr.test_gut.pre_run_script = "res://test/resources/awaiting_pre_run_script.gd"
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
gr.test_gut.test_scripts()
await wait_for_signal(gr.test_gut.start_run, 3, "It should take exactly 1 second.")
assert_true(gr.test_gut.get_pre_run_script_instance().awaited, "Pre-run script awaited.")
await wait_seconds(1)
func test_awaiting_in_the_post_hook_script():
var pre_run_script = load("res://test/resources/awaiting_post_run_script.gd")
gr.test_gut.post_run_script = "res://test/resources/awaiting_post_run_script.gd"
gr.test_gut.add_script(SAMPLES_DIR + 'test_sample_all_passed.gd')
gr.test_gut.test_scripts()
await wait_for_signal(gr.test_gut.end_run, 3, "It should take exactly 1 second.")
assert_true(gr.test_gut.get_post_run_script_instance().awaited, "Post-run script awaited.")
await wait_seconds(.1)
# ------------------------------
# Parameterized Test Tests
# ------------------------------
const TEST_WITH_PARAMETERS = 'res://test/resources/parsing_and_loading_samples/test_with_parameters.gd'
func _get_test_script_object_of_type(the_gut, the_type):
var objs = gr.test_gut._test_script_objects
var obj = null
for i in range(objs.size()):
if(is_instance_of(objs[i], the_type)):
obj = objs[i]
print('- ', _str(objs[i]))
return obj
func test_can_run_tests_with_parameters():
gr.test_gut.add_script(TEST_WITH_PARAMETERS)
gr.test_gut.unit_test_name = 'test_has_one_defaulted_parameter'
await run_tests()
assert_eq(gr.test_gut.get_pass_count(), 1, 'pass count')
assert_eq(gr.test_gut.get_test_count(), 1, 'test count')
func test_too_many_parameters_generates_an_error():
gr.test_gut.add_script(TEST_WITH_PARAMETERS)
gr.test_gut.unit_test_name = 'test_has_two_parameters'
await run_tests()
assert_tracked_gut_error(gr.test_gut, 1)
assert_eq(gr.test_gut.get_test_count(), 0, 'test count')
func test_parameterized_tests_are_called_multiple_times():
gr.test_gut.add_script(TEST_WITH_PARAMETERS)
gr.test_gut.unit_test_name = 'test_has_three_values_for_parameters'
await run_tests()
assert_eq(gr.test_gut.get_pass_count(), 3)
func test_when_use_parameters_is_not_called_then_error_is_generated():
gr.test_gut.add_script(TEST_WITH_PARAMETERS)
gr.test_gut.unit_test_name = 'test_does_not_use_use_parameters'
await run_tests()
assert_tracked_gut_error(gr.test_gut)
assert_tracked_gut_error(gr.test_gut, 1)
assert_eq(gr.test_gut.get_fail_count(), 1)
# if you really think about this, it is a very very inception like test.
func test_parameterized_test_that_yield_are_called_correctly():
gr.test_gut.add_script(TEST_WITH_PARAMETERS)
gr.test_gut.unit_test_name = 'test_three_values_and_a_yield'
await run_tests()
assert_eq(gr.test_gut.get_pass_count(), 3)
func test_parameterized_test_calls_before_each_before_each_test():
gr.test_gut.add_script(TEST_WITH_PARAMETERS)
gr.test_gut.inner_class_name = 'TestWithBeforeEach'
gr.test_gut.run_tests()
assert_eq(gr.test_gut.get_pass_count(), 3)
var obj = _get_test_script_object_of_type(gr.test_gut, load(TEST_WITH_PARAMETERS).TestWithBeforeEach)
assert_eq(obj.before_count, 3, 'test class: before_count')
await wait_seconds(1)
func test_parameterized_test_calls_after_each_after_each_test():
gr.test_gut.add_script(TEST_WITH_PARAMETERS)
gr.test_gut.inner_class_name = 'TestWithAfterEach'
gr.test_gut.run_tests()
assert_eq(gr.test_gut.get_pass_count(), 3)
var obj = _get_test_script_object_of_type(gr.test_gut, load(TEST_WITH_PARAMETERS).TestWithAfterEach)
assert_eq(obj.after_count, 3, 'test class: after_count')
await wait_seconds(1)
# ------------------------------
# Asserting in before_all and after_all
# ------------------------------
func test_passing_asserts_made_in_before_all_are_counted():
gr.test_gut.add_script('res://test/resources/has_asserts_in_beforeall_and_afterall.gd')
gr.test_gut.inner_class_name = 'TestPassingBeforeAllAssertNoOtherTests'
await run_tests()
assert_eq(gr.test_gut.get_assert_count(), 1, 'assert count')
assert_eq(gr.test_gut.get_pass_count(), 1, 'pass count')
func test_failing_asserts_made_in_after_all_are_counted():
gr.test_gut.add_script('res://test/resources/has_asserts_in_beforeall_and_afterall.gd')
gr.test_gut.inner_class_name = 'TestFailingAfterAllAssertNoOtherTests'
await run_tests()
assert_eq(gr.test_gut.get_assert_count(), 1, 'assert count')
assert_eq(gr.test_gut.get_fail_count(), 1, 'fail count')
func test_before_all_after_all_printing():
gr.test_gut.add_script('res://test/resources/has_asserts_in_beforeall_and_afterall.gd')
gr.test_gut.inner_class_name = 'TestHasBeforeAllAfterAllAndSomeTests'
await run_tests()
assert_eq(gr.test_gut.get_pass_count(), 4, 'pass count')
assert_eq(gr.test_gut.get_fail_count(), 4, 'fail count')
assert_eq(gr.test_gut.get_assert_count(), 8, 'assert count`')
func test_before_all_after_all_printing_all_classes_in_script():
gr.test_gut.add_script('res://test/resources/has_asserts_in_beforeall_and_afterall.gd')
await run_tests()
assert_eq(gr.test_gut.get_pass_count(), 10, 'pass count')
assert_eq(gr.test_gut.get_fail_count(), 10, 'fail count')
assert_eq(gr.test_gut.get_assert_count(), 20, 'assert count`')
class TestReworkedEverythingElse:
extends GutInternalTester
var _gut = null
func before_all():
verbose = false
DynamicGutTest.should_print_source = verbose
func before_each():
_gut = add_child_autofree(new_gut(verbose))
func test_passing_asserts_made_in_after_all_are_counted():
var src = """
func after_all():
assert_true(true)
func test_nothing():
assert_eq(1, 1)
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.passing, 2, 'passing asserts')
func test_failing_asserts_made_in_before_all_are_counted():
var src = """
func after_all():
assert_true(false)
func test_nothing():
assert_eq(1, 1)
pending()
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 1, 'one failing')
func test_failing_asserts_made_in_before_all_are_in_the_summary():
var src = """
func before_all():
assert_true(false)
func test_nothing():
assert_eq(1, 1)
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src)
var t = await s.run_tests_in_gut_await(_gut)
_gut.get_summary().log_end_run()
assert_eq(t.failing, 1, 'one failing')
assert_has(_gut.get_logger().get_log_entries('failed'), "before_all/after_all assert failed")
func test_failing_asserts_made_in_after_all_are_in_the_summary():
var src = """
func after_all():
assert_true(false)
func test_nothing():
assert_eq(1, 1)
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src)
var t = await s.run_tests_in_gut_await(_gut)
_gut.get_summary().log_end_run()
assert_eq(t.failing, 1, 'one failing')
assert_has(_gut.get_logger().get_log_entries('failed'), "before_all/after_all assert failed")
func test_failing_asserts_made_in_before_each_cause_test_it_is_before_to_fail():
var src = """
func before_each():
assert_true(false, 'before_each_assert')
func test_nothing():
assert_eq(1, 1)
func test_something():
assert_eq(1, 1)
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 2, 'failing asserts')
assert_eq(t.failing_tests, 2, 'failing tests')
func test_failing_asserts_made_in_after_each_cause_test_it_is_after_to_fail():
var src = """
func after_each():
assert_true(false, 'after_each assert')
func test_nothing():
assert_eq(1, 1)
func test_something():
assert_eq(1, 1)
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src)
var t = await s.run_tests_in_gut_await(_gut)
assert_eq(t.failing, 2, 'failing asserts')
assert_eq(t.failing_tests, 2, 'failing tests')
class TestGutHookOrder:
extends GutInternalTester
var _gut = null
func before_each():
_gut = add_child_autofree(new_gut(verbose))
func test_signal_order():
var events: Array[String] = []
_gut.set_meta("test_signal_order", events)
_gut.start_script.connect(func(_x): events.append("signal start_script"))
_gut.start_test.connect(func(_x): events.append("signal start_test"))
_gut.end_test.connect(func(): events.append("signal end_test"))
_gut.end_script.connect(func(): events.append("signal end_script"))
var src = """
func before_all():
gut.get_meta("test_signal_order").append("hook before_all")
func before_each():
gut.get_meta("test_signal_order").append("hook before_each")
func after_all():
gut.get_meta("test_signal_order").append("hook after_all")
func after_each():
gut.get_meta("test_signal_order").append("hook after_each")
func test_nothing():
pass_test("")
"""
var s = autofree(DynamicGutTest.new())
s.add_source(src)
await s.run_tests_in_gut_await(_gut)
assert_eq(_gut.get_meta("test_signal_order"), [
"signal start_script",
"hook before_all",
"hook before_each",
"signal start_test",
"hook after_each",
"signal end_test",
"hook after_all",
"signal end_script",
])