chore: add GUT test framework
This commit is contained in:
975
addons/gut/test/unit/test_gut.gd
Normal file
975
addons/gut/test/unit/test_gut.gd
Normal file
@@ -0,0 +1,975 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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",
|
||||
])
|
||||
Reference in New Issue
Block a user