e7299b17e9
Stack installed: - netfox v1.35.3 (core + extras + noray + internals) - godot-jolt v0.16.0-stable Architecture: - Server: ENet transport (works headless, no netfox deps) - Client/Editor: netfox rollback (RollbackSynchronizer, TickInterpolator) New/modified: - docs/migration-netfox-plan.md — migration architecture - scripts/network/network_manager.gd — netfox-aware ENet fallback - scripts/network/player.gd — clean base player - client/characters/player_netfox.gd — rollback player w/ WeaponManager - client/characters/input/player_net_input.gd — BaseNetInput subclass - client/characters/character/fps_character_controller.gd — netfox input feed - client/weapons/ — weapon data, registry, TacticalWeaponHitscan, WeaponManager - client/scripts/round_replicator.gd — client-side round state bridge - server/scripts/round_manager.gd — improved state machine - server/scripts/plugin_api/plugin_manager.gd — refined plugin system - config: enemy_tag, ally_tag for meatball targeting Removed: old C++ SimulationServer GDExtension (replaced by netfox rollback)
112 lines
3.9 KiB
GDScript
112 lines
3.9 KiB
GDScript
extends Node
|
|
class_name TestUtils
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# TestUtils — Minimal assertion framework for headless Godot unit tests
|
|
#
|
|
# Usage:
|
|
# var tu = TestUtils.new()
|
|
# add_child(tu)
|
|
# tu.describe("Movement validation")
|
|
# tu.assert_eq(result.x, 5, "x should be 5")
|
|
# tu.assert_true(valid, "should be valid")
|
|
# tu.finish() # prints summary
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
var _passed: int = 0
|
|
var _failed: int = 0
|
|
var _current_suite: String = ""
|
|
var _assertions_in_suite: int = 0
|
|
|
|
func describe(name: String) -> void:
|
|
"""Start a named test suite/section."""
|
|
_current_suite = name
|
|
_assertions_in_suite = 0
|
|
print("\n === " + name + " ===")
|
|
|
|
func assert_eq(got, expected, msg: String = "") -> void:
|
|
_assertions_in_suite += 1
|
|
if got == expected:
|
|
_passed += 1
|
|
else:
|
|
_failed += 1
|
|
var detail = "got '%s', expected '%s'" % [str(got), str(expected)]
|
|
print(" [FAIL] " + msg + " — " + detail)
|
|
|
|
func assert_ne(got, unexpected, msg: String = "") -> void:
|
|
_assertions_in_suite += 1
|
|
if got != unexpected:
|
|
_passed += 1
|
|
else:
|
|
_failed += 1
|
|
print(" [FAIL] " + msg + " — got '%s' which matches unexpected value" % str(got))
|
|
|
|
func assert_true(cond: bool, msg: String = "") -> void:
|
|
_assertions_in_suite += 1
|
|
if cond:
|
|
_passed += 1
|
|
else:
|
|
_failed += 1
|
|
print(" [FAIL] " + msg + " — expected true, got false")
|
|
|
|
func assert_false(cond: bool, msg: String = "") -> void:
|
|
_assertions_in_suite += 1
|
|
if not cond:
|
|
_passed += 1
|
|
else:
|
|
_failed += 1
|
|
print(" [FAIL] " + msg + " — expected false, got true")
|
|
|
|
func assert_almost_eq(got: float, expected: float, tolerance: float = 0.001, msg: String = "") -> void:
|
|
_assertions_in_suite += 1
|
|
if abs(got - expected) <= tolerance:
|
|
_passed += 1
|
|
else:
|
|
_failed += 1
|
|
var detail = "got %.4f, expected %.4f ± %.4f" % [got, expected, tolerance]
|
|
print(" [FAIL] " + msg + " — " + detail)
|
|
|
|
func assert_violation(result: Dictionary, category: int, msg: String = "") -> void:
|
|
"""Assert the validation result contains at least one violation of the given category."""
|
|
_assertions_in_suite += 1
|
|
for v in result.get("violations", []):
|
|
if v.get("category", -1) == category:
|
|
_passed += 1
|
|
return
|
|
_failed += 1
|
|
print(" [FAIL] " + msg + " — expected violation category " + str(category) + ", none found")
|
|
|
|
func assert_no_violation(result: Dictionary, msg: String = "") -> void:
|
|
"""Assert the validation result has zero violations."""
|
|
_assertions_in_suite += 1
|
|
var count = result.get("violations", []).size()
|
|
if count == 0:
|
|
_passed += 1
|
|
else:
|
|
_failed += 1
|
|
print(" [FAIL] " + msg + " — expected 0 violations, got " + str(count) + ": " + str(result["violations"]))
|
|
|
|
func assert_corrected(result: Dictionary, field: String, msg: String = "") -> void:
|
|
"""Assert the result's corrected dict has a different value for `field` than the original."""
|
|
_assertions_in_suite += 1
|
|
if result.get("corrected", {}).has(field):
|
|
_passed += 1
|
|
else:
|
|
_failed += 1
|
|
print(" [FAIL] " + msg + " — expected correction in field '%s', none found" % field)
|
|
|
|
func finish() -> void:
|
|
"""Print test summary and return exit code via OS."""
|
|
var total = _passed + _failed
|
|
print("\n ──────────────────────────────────────")
|
|
print(" Suite: " + _current_suite)
|
|
print(" Passed: %d / %d" % [_passed, total])
|
|
if _failed > 0:
|
|
print(" FAILED: %d test(s)" % _failed)
|
|
else:
|
|
print(" All passed.")
|
|
print("")
|
|
|
|
func exit_code() -> int:
|
|
return 0 if _failed == 0 else 1
|