Files
gplan-2/game/tests/unit/contracts/source_guard_test.gd
T

101 lines
5.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# tests/unit/contracts/source_guard_test.gd — 仓库自身源码契约单元测试宿主
# 断言面 = 两套(批 B 断言迁移自 smoke_all.gd,G12 施工组;被测对象=仓库自身源文件,
# 主会话裁决五新设 source_guard 子目录归「契约」语义):
# 类型闸(project.godot warning 配置在位 + 审计器闸机接线)
# 卫生件 grep 闸(产品码字符串 Callable/死代码/GP 影子账残留零命中 + 层序落位单口)
# 协议照单元侧惯例:extends SceneTree+Kit.check 主通道;被测面是仓库文件直读
# (FileAccess/DirAccess/project.godot 设置),零表夹具零 RNG。
# 运行:godot --headless --path game --script res://tests/unit/contracts/source_guard_test.gd
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
func _init() -> void:
# 套执行序沿源编排序(_split_tail 尾段调用序)
_smoke_type_gate()
_smoke_hygiene_gates()
quit(Kit.finish("source_guard_test"))
# ---- 类型闸断言(V7——project.godot warning 配置在位〔源码断言〕+审计器脚本在位;
# 审计器归零判定走 static_scan S9 闸机,此处兜配置面)----
func _smoke_type_gate() -> void:
# oracle: design/engineering/code-conventions.md:41 (封装边界——类型闸源码扫描组)
var pj: String = FileAccess.get_file_as_string("res://project.godot")
Kit.check("V7 类型闸 warning 配置在位(untyped_declaration=1 显形)〔迭代27 T5〕",
pj.contains("gdscript/warnings/untyped_declaration=1"))
var scan_src: String = FileAccess.get_file_as_string("res://tests/static_scan.sh")
Kit.check("V7 类型审计器闸机在位(static_scan S9 → tools/audit_type_hints.py)〔迭代27 T5〕",
scan_src.contains("audit_type_hints.py"))
# ---- 卫生件 grep 闸(V8——死代码/字符串 Callable/影子账残留零命中;源码扫描面)----
func _smoke_hygiene_gates() -> void:
# oracle: design/engineering/code-conventions.md:54 (模态覆盖层层级与登记纪律——卫生闸组)
var callables := 0
for d in ["res://core", "res://presentation", "res://data"]:
var dir := DirAccess.open(d)
if dir == null:
continue
dir.list_dir_begin()
var f := dir.get_next()
while f != "":
if dir.current_is_dir() and not f.begins_with("."):
var sub := DirAccess.open(d + "/" + f)
if sub != null:
sub.list_dir_begin()
var g := sub.get_next()
while g != "":
if g.ends_with(".gd"):
var t: String = FileAccess.get_file_as_string(d + "/" + f + "/" + g)
callables += t.count("Callable(self, \"")
g = sub.get_next()
elif f.ends_with(".gd"):
var t2: String = FileAccess.get_file_as_string(d + "/" + f)
callables += t2.count("Callable(self, \"")
f = dir.get_next()
Kit.check("V8 字符串 Callable 产品码零命中(实测应 0——方法引用换装闸)〔迭代27 T6〕",
callables == 0)
Kit.check("V8 FeatsEngine 整文件退役(class_name 注销=文件删除)〔迭代27 T6〕",
not FileAccess.file_exists("res://core/feats_engine.gd"))
var bo: String = FileAccess.get_file_as_string("res://core/battle_orchestrator.gd")
Kit.check("V8 死代码抽查·_attune_row 零残留〔迭代27 T6〕",
not bo.contains("func _attune_row"))
var sm: String = FileAccess.get_file_as_string("res://core/save_manager.gd")
Kit.check("V8 死代码抽查·attunement_counters 零残留〔迭代27 T6〕",
not sm.contains("func attunement_counters"))
var mv: String = FileAccess.get_file_as_string("res://presentation/main.gd")
Kit.check("V8 GP 影子账残留零命中(main 源码面——文件已删+委托缝已拆)〔迭代27 T3〕",
not mv.contains("gp_shadow_ledger") and not mv.contains("_gp_ledger"))
# V1d 层序落位单口:注册面建件后扫全 presentation——move_child 只允许出现在 ui_registry.gd 的
# apply_layer_contract 函数体内(合同装配单口——出现第二处落位=硬编码层序,须改合同表而非加调用)
var scanned: int = 0
var outside_calls: int = 0
for fn_path: String in _list_scripts("res://presentation"):
var src: String = FileAccess.get_file_as_string(fn_path)
scanned += 1
var fn_start: int = src.find("func apply_layer_contract") if fn_path.contains("ui_registry.gd") else -1
var fn_end: int = src.find("\nfunc ", fn_start + 10) if fn_start >= 0 else -1
if fn_end < 0:
fn_end = src.length()
outside_calls += (src.substr(0, fn_start) + src.substr(fn_end)).count("move_child(")
Kit.check("V1d 层序落位单口(presentation %d 文件——apply_layer_contract 之外 move_child 调用点=%d 应=0)〔注册面 V1〕" % [scanned, outside_calls],
scanned > 0 and outside_calls == 0)
# 目录 .gd 清单(DirAccess 平铺——presentation 无子目录;扫描面>0 前置由调用侧断言承载)
static func _list_scripts(dir_path: String) -> PackedStringArray:
var out: PackedStringArray = PackedStringArray()
var dir := DirAccess.open(dir_path)
if dir == null:
return out
dir.list_dir_begin()
var fname := dir.get_next()
while fname != "":
if fname.ends_with(".gd"):
out.append(dir_path + "/" + fname)
fname = dir.get_next()
dir.list_dir_end()
return out