add unit tests

saveload fixes
This commit is contained in:
2025-09-27 12:17:14 +04:00
parent 3e960a955c
commit dd0c1a123c
31 changed files with 3400 additions and 282 deletions

220
tests/helpers/TestHelper.gd Normal file
View File

@@ -0,0 +1,220 @@
extends RefCounted
class_name TestHelper
## Common test utilities and assertions for Skelly project testing
##
## Provides standardized testing functions, assertions, and utilities
## to ensure consistent test behavior across all test files.
## Test result tracking
static var tests_run := 0
static var tests_passed := 0
static var tests_failed := 0
## Performance tracking
static var test_start_time := 0.0
static var performance_data := {}
## Print test section header with consistent formatting
static func print_test_header(test_name: String):
print("\n=== Testing %s ===" % test_name)
tests_run = 0
tests_passed = 0
tests_failed = 0
test_start_time = Time.get_unix_time_from_system()
## Print test section footer with results summary
static func print_test_footer(test_name: String):
var end_time = Time.get_unix_time_from_system()
var duration = end_time - test_start_time
print("\n--- %s Results ---" % test_name)
print("Tests Run: %d" % tests_run)
print("Passed: %d" % tests_passed)
print("Failed: %d" % tests_failed)
print("Duration: %.3f seconds" % duration)
if tests_failed == 0:
print("✅ All tests PASSED")
else:
print("%d tests FAILED" % tests_failed)
print("=== %s Complete ===" % test_name)
## Assert that a condition is true
static func assert_true(condition: bool, message: String = ""):
tests_run += 1
if condition:
tests_passed += 1
print("✅ PASS: %s" % message)
else:
tests_failed += 1
print("❌ FAIL: %s" % message)
## Assert that a condition is false
static func assert_false(condition: bool, message: String = ""):
assert_true(not condition, message)
## Assert that two values are equal
static func assert_equal(expected, actual, message: String = ""):
var condition = expected == actual
var full_message = message
if not full_message.is_empty():
full_message += " "
full_message += "(Expected: %s, Got: %s)" % [str(expected), str(actual)]
assert_true(condition, full_message)
## Assert that two values are not equal
static func assert_not_equal(expected, actual, message: String = ""):
var condition = expected != actual
var full_message = message
if not full_message.is_empty():
full_message += " "
full_message += "(Should not equal: %s, Got: %s)" % [str(expected), str(actual)]
assert_true(condition, full_message)
## Assert that a value is null
static func assert_null(value, message: String = ""):
assert_true(value == null, message + " (Should be null, got: %s)" % str(value))
## Assert that a value is not null
static func assert_not_null(value, message: String = ""):
assert_true(value != null, message + " (Should not be null)")
## Assert that a value is within a range
static func assert_in_range(value: float, min_val: float, max_val: float, message: String = ""):
var condition = value >= min_val and value <= max_val
var full_message = "%s (Value: %f, Range: %f-%f)" % [message, value, min_val, max_val]
assert_true(condition, full_message)
## Assert that two floating-point values are approximately equal (with tolerance)
static func assert_float_equal(expected: float, actual: float, tolerance: float = 0.0001, message: String = ""):
# Handle special cases: both infinity, both negative infinity, both NaN
if is_inf(expected) and is_inf(actual):
var condition = (expected > 0) == (actual > 0) # Same sign of infinity
var full_message = "%s (Both infinity values: Expected: %f, Got: %f)" % [message, expected, actual]
assert_true(condition, full_message)
return
if is_nan(expected) and is_nan(actual):
var full_message = "%s (Both NaN values: Expected: %f, Got: %f)" % [message, expected, actual]
assert_true(true, full_message) # Both NaN is considered equal
return
# Normal floating-point comparison
var difference = abs(expected - actual)
var condition = difference <= tolerance
var full_message = "%s (Expected: %f, Got: %f, Difference: %f, Tolerance: %f)" % [message, expected, actual, difference, tolerance]
assert_true(condition, full_message)
## Assert that an array contains a specific value
static func assert_contains(array: Array, value, message: String = ""):
var condition = value in array
var full_message = "%s (Array: %s, Looking for: %s)" % [message, str(array), str(value)]
assert_true(condition, full_message)
## Assert that an array does not contain a specific value
static func assert_not_contains(array: Array, value, message: String = ""):
var condition = not (value in array)
var full_message = "%s (Array: %s, Should not contain: %s)" % [message, str(array), str(value)]
assert_true(condition, full_message)
## Assert that a dictionary has a specific key
static func assert_has_key(dict: Dictionary, key, message: String = ""):
var condition = dict.has(key)
var full_message = "%s (Dictionary keys: %s, Looking for: %s)" % [message, str(dict.keys()), str(key)]
assert_true(condition, full_message)
## Assert that a file exists
static func assert_file_exists(path: String, message: String = ""):
var condition = FileAccess.file_exists(path)
var full_message = "%s (Path: %s)" % [message, path]
assert_true(condition, full_message)
## Assert that a file does not exist
static func assert_file_not_exists(path: String, message: String = ""):
var condition = not FileAccess.file_exists(path)
var full_message = "%s (Path: %s)" % [message, path]
assert_true(condition, full_message)
## Performance testing - start timing
static func start_performance_test(test_id: String):
performance_data[test_id] = Time.get_unix_time_from_system()
## Performance testing - end timing and validate
static func end_performance_test(test_id: String, max_duration_ms: float, message: String = ""):
if not performance_data.has(test_id):
assert_true(false, "Performance test '%s' was not started" % test_id)
return
var start_time = performance_data[test_id]
var end_time = Time.get_unix_time_from_system()
var duration_ms = (end_time - start_time) * 1000.0
var condition = duration_ms <= max_duration_ms
var full_message = "%s (Duration: %.2fms, Max: %.2fms)" % [message, duration_ms, max_duration_ms]
assert_true(condition, full_message)
performance_data.erase(test_id)
## Create a temporary test file with content
static func create_temp_file(filename: String, content: String = "") -> String:
var temp_path = "user://test_" + filename
var file = FileAccess.open(temp_path, FileAccess.WRITE)
if file:
file.store_string(content)
file.close()
return temp_path
## Clean up temporary test file
static func cleanup_temp_file(path: String):
if FileAccess.file_exists(path):
DirAccess.remove_absolute(path)
## Create invalid JSON content for testing
static func create_invalid_json() -> String:
return '{"invalid": json, missing_quotes: true, trailing_comma: true,}'
## Create valid test JSON content
static func create_valid_json() -> String:
return '{"test_key": "test_value", "test_number": 42, "test_bool": true}'
## Wait for a specific number of frames
static func wait_frames(frames: int, node: Node):
for i in range(frames):
await node.get_tree().process_frame
## Mock a simple function call counter
class MockCallCounter:
var call_count := 0
var last_args := []
func call_function(args: Array = []):
call_count += 1
last_args = args.duplicate()
func reset():
call_count = 0
last_args.clear()
## Create a mock call counter for testing
static func create_mock_counter() -> MockCallCounter:
return MockCallCounter.new()
## Validate that an object has expected properties
static func assert_has_properties(object: Object, properties: Array, message: String = ""):
for property in properties:
var condition = property in object
var full_message = "%s - Missing property: %s" % [message, property]
assert_true(condition, full_message)
## Validate that an object has expected methods
static func assert_has_methods(object: Object, methods: Array, message: String = ""):
for method in methods:
var condition = object.has_method(method)
var full_message = "%s - Missing method: %s" % [message, method]
assert_true(condition, full_message)
## Print a test step with consistent formatting
static func print_step(step_name: String):
print("\n--- Test: %s ---" % step_name)

View File

@@ -0,0 +1 @@
uid://du7jq8rtegu8o