79 lines
2.5 KiB
GDScript
79 lines
2.5 KiB
GDScript
extends SceneTree
|
||
|
||
# ============================================================================
|
||
# test_runner.gd —— core 层无头单测入口(GUT 未装前的自包含方案;W3 换 GUT)
|
||
#
|
||
# 运行:godot --headless --path . -s res://tests/unit/test_runner.gd
|
||
# 退出码:0 = 全过;1 = 有失败;2 = 数据/环境错误。
|
||
# 每个用例文件 = tests/unit/cases/*.gd,契约:func run(a) -> void(a = TestAssert)。
|
||
# ============================================================================
|
||
|
||
class TestAssert extends RefCounted:
|
||
var failures: Array[String] = []
|
||
var checks := 0
|
||
|
||
func check(cond: bool, msg: String) -> void:
|
||
checks += 1
|
||
if not cond:
|
||
failures.append(msg)
|
||
|
||
func eq(got: Variant, want: Variant, msg: String) -> void:
|
||
checks += 1
|
||
if got != want:
|
||
failures.append("%s:got %s want %s" % [msg, str(got), str(want)])
|
||
|
||
func near(got: float, want: float, tol: float, msg: String) -> void:
|
||
checks += 1
|
||
if absf(float(got) - float(want)) > tol:
|
||
failures.append("%s:got %.6g want %.6g(tol %g)" % [msg, got, want, tol])
|
||
|
||
func true_(cond: bool, msg: String) -> void:
|
||
check(cond, msg)
|
||
|
||
func false_(cond: bool, msg: String) -> void:
|
||
check(not cond, msg)
|
||
|
||
|
||
const CASE_FILES: Array[String] = [
|
||
"res://tests/unit/cases/test_number_format.gd",
|
||
"res://tests/unit/cases/test_economy_formulas.gd",
|
||
"res://tests/unit/cases/test_balance_data.gd",
|
||
"res://tests/unit/cases/test_economy_state.gd",
|
||
]
|
||
|
||
|
||
func _initialize() -> void:
|
||
var data := BalanceData.load_from_dir("res://data/build/")
|
||
if not data.load_errors.is_empty():
|
||
for e in data.load_errors:
|
||
printerr("test_runner:数据错误:%s" % e)
|
||
quit(2)
|
||
return
|
||
var total_checks := 0
|
||
var total_fail: Array[String] = []
|
||
for path in CASE_FILES:
|
||
var script: Variant = load(path)
|
||
if script == null:
|
||
printerr("test_runner:用例缺失 %s" % path)
|
||
quit(2)
|
||
return
|
||
var case_obj: Variant = script.new()
|
||
if not (case_obj is RefCounted) or not case_obj.has_method("run"):
|
||
printerr("test_runner:用例契约不符 %s" % path)
|
||
quit(2)
|
||
return
|
||
var a := TestAssert.new()
|
||
case_obj.run(a, data)
|
||
total_checks += a.checks
|
||
for f in a.failures:
|
||
total_fail.append("%s → %s" % [path.get_file(), f])
|
||
print("test_runner:%s:%d 检查,%d 失败" % [path.get_file(), a.checks, a.failures.size()])
|
||
if total_fail.is_empty():
|
||
print("test_runner:PASS(%d 检查全过)" % total_checks)
|
||
quit(0)
|
||
return
|
||
print("test_runner:FAIL(%d/%d)" % [total_fail.size(), total_checks])
|
||
for f in total_fail:
|
||
printerr(" ✗ %s" % f)
|
||
quit(1)
|