Files
gplan-2/game/tests/unit/presentation/presentation_encapsulation_sweep_test.gd
T

201 lines
10 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/presentation/presentation_encapsulation_sweep_test.gd — 架构审查 presentation 层封装修复·四族结构性零命中扫描宿主
# 断言面(全部带「扫描面非空前置+正锚」——非空防扫描面为空静默绿,正锚证新面在用):
# V1 A1 三内部数据类(level_map_screen/_MapState、battle_end_overlay/_EndOverlayState、debug_overlay/_DebugState):
# 非空+class 正锚+实例私取模式零命中(_map\._/st\._)+42 公开字段逐个正锚+类名文件外零消费者
# V2 A2 拆件子件(standee_body→vitals/motion、board_view→board_hover):
# 非空+子件私取模式零命中(st\._/mo\._/_hover_comp\._)+新公开面正锚
# V3 B 组合根交接面(main+五分文件):
# 非空+root\._ 与 MainXxxFlow\._ 私取零命中+束消费/新公开面正锚
# (C 断言组随同批 T5 落地——本件四组齐后为「presentation 私取 sweep 恒零」结构性主证)
# 手法沿 encapsulation_sweep_test(5.1 卡)源 token 扫描先例,不并件——扫描面与尾缀语义本批特有(卡 §12 同款论证)。
# 运行:godot --headless --path game --script res://tests/unit/presentation/presentation_encapsulation_sweep_test.gd
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
const A1_FILES := {
"res://presentation/views/level_map_screen.gd": {
"cls": "_MapState", "inst": "_map",
"fields": ["abandon_timer", "attr_value", "bonus_row", "bonus_value", "canvas", "drawn_disp",
"feat_value", "gp_value", "hp_bar", "hp_value", "layer_decor", "level_label", "runner", "title"],
},
"res://presentation/views/battle_end_overlay.gd": {
"cls": "_EndOverlayState", "inst": "st",
"fields": ["board_rect", "bonus_name", "bonus_row_l", "bonus_val", "cele", "crest_bricks", "dim",
"fx_counts", "ks_name", "ks_row", "ks_val", "rows_box", "seal", "verdict"],
},
"res://presentation/views/debug_overlay.gd": {
"cls": "_DebugState", "inst": "st",
"fields": ["atk", "atk_val", "base_atk", "base_hp", "ctx_locked", "db", "eff", "hp", "hp_val",
"run_data", "run_labels", "sim_count", "sim_out", "sim_running"],
},
}
func _init() -> void:
# oracle: design/engineering/code-conventions.md:41 (presentation 封装——四族零命中扫描组)
_sweep_a1_data_classes()
_sweep_a2_subparts()
_sweep_b_combo_root()
_sweep_c_misc()
quit(Kit.finish("presentation_encapsulation_sweep_test"))
# 源文件读取+非空前置(返 "" 即已判红——调用侧跳过后续正则断言)
func _src(path: String) -> String:
# oracle: design/engineering/code-conventions.md:41 (源码采样组)
var src := FileAccess.get_file_as_string(path)
Kit.check("扫描面非空前置:%s" % path, src.length() > 0)
return src
# 正则零命中断言(-pattern 意图写入断言文案——可 grep 定位;suffix 区分族组)
func _zero(pattern: String, src: String, label: String, suffix: String = "pres封装 V1") -> void:
# oracle: design/engineering/code-conventions.md:41 (零命中扫描组)
var re := RegEx.create_from_string(pattern)
Kit.check("%s:%s 零命中〔%s〕" % [label, pattern, suffix], re.search(src) == null)
# 正锚在案断言(正则命中 ≥1——新公开面/新形态在用;suffix 区分族组)
func _anchor(pattern: String, src: String, label: String, suffix: String = "pres封装 V1") -> void:
# oracle: design/engineering/code-conventions.md:41 (正锚存在组)
var re := RegEx.create_from_string(pattern)
Kit.check("%s:%s 正锚在案〔%s〕" % [label, pattern, suffix], re.search(src) != null)
# ② V2 A2 拆件子件(行为归位子件公开方法、读走公开读法、身份构造交付)
func _sweep_a2_subparts() -> void:
# oracle: design/engineering/code-conventions.md:41 (A2 子件封装扫描组)
var body := _src("res://presentation/views/standee_body.gd")
if body.length() > 0:
_zero("\\bst\\._|\\bmo\\._", body, "子件私取模式零命中(standee_body)", "pres封装 V2")
_anchor("\\.configure_identity\\(", body, "身份构造交付正锚(standee_body)", "pres封装 V2")
_anchor("\\.play_hp_flash\\(", body, "闪白归位正锚(standee_body)", "pres封装 V2")
_anchor("\\.show_intent\\(|\\.hide_intent\\(", body, "意图行为正锚(standee_body)", "pres封装 V2")
_anchor("mo\\.portrait_sprite\\(\\)", body, "绘制读口正锚(standee_body)", "pres封装 V2")
var board := _src("res://presentation/views/board_view.gd")
if board.length() > 0:
_zero("_hover_comp\\._", board, "子件私取模式零命中(board_view)", "pres封装 V2")
_anchor("_hover_comp\\.(rebind|reset|hover_at|detect_chord|refresh_after_paint|on_shell_mouse_exited|hover_pos)",
board, "悬停七口正锚(board_view)", "pres封装 V2")
# ③ V3 B 组合根交接面(main+五分文件——成对单行转发梯换代为公开入口+Callable 束)
func _sweep_b_combo_root() -> void:
# oracle: design/engineering/code-conventions.md:41 (组合根束封装扫描组)
var b_files := {
"res://presentation/main.gd": false,
"res://presentation/main_ui_build.gd": true,
"res://presentation/main_adventure_flow.gd": true,
"res://presentation/main_overlay_flow.gd": true,
"res://presentation/main_debug_flow.gd": true,
"res://presentation/main_esc_flow.gd": true,
}
for path in b_files:
var src := _src(path)
if src.length() == 0:
continue
if b_files[path]:
_zero("root\\._[a-zA-Z]", src, "root 私取零命中(%s)" % path.get_file(), "pres封装 V3")
_zero("Main(Overlay|Adventure|Debug|Esc)Flow\\._[a-zA-Z]", src, "分文件私取零命中(%s)" % path.get_file(), "pres封装 V3")
var main_src := FileAccess.get_file_as_string("res://presentation/main.gd")
if main_src.length() > 0:
for fn in ["set_treasure_ui", "treasure_ui", "set_bonus", "bonus", "set_camp_pres", "camp_pres",
"set_debug", "debug", "set_tune_sim", "tune_sim", "set_confirm_consumer", "refresh_presentations",
"quit_game", "begin_session", "open_save_select", "seed_value"]:
_anchor("func %s\\(" % fn, main_src, "main 公开口正锚(%s)" % fn, "pres封装 V3")
var wiring := FileAccess.get_file_as_string("res://presentation/main_ui_build.gd")
if wiring.length() > 0:
_anchor("handlers\\[\"board_action\"\\]", wiring, "束消费正锚(ui_build)", "pres封装 V3")
_anchor("root\\.set_treasure_ui\\(", wiring, "句柄设置口正锚(ui_build)", "pres封装 V3")
_anchor("root\\.camp_pres\\(\\)\\.", wiring, "句柄读投影正锚(ui_build)", "pres封装 V3")
var adv := FileAccess.get_file_as_string("res://presentation/main_adventure_flow.gd")
if adv.length() > 0:
_anchor("handlers\\[\"adventure_finished\"\\]", adv, "束消费正锚(adventure)", "pres封装 V3")
_anchor("root\\.bonus\\(\\)\\.", adv, "句柄读投影正锚(adventure)", "pres封装 V3")
# ④ V4 C 杂项(UiKit 私有工厂公开化+cell_mark 构造交付)
func _sweep_c_misc() -> void:
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
var offenders: PackedStringArray = []
var scanned := 0
_walk_gd_for("res://", "UiKit\\._[a-zA-Z]", "res://presentation/views/ui_kit.gd", offenders)
scanned = _scanned
Kit.check("UiKit 私有工厂全库零外调(扫描面 %d 件非空前置;越界=%s)〔pres封装 V4〕"
% [scanned, str(offenders)], offenders.is_empty())
var cm := _src("res://presentation/views/cell_mark_overlay.gd")
if cm.length() > 0:
_zero("_owner\\._", cm, "宿主私有回引零命中(cell_mark_overlay)", "pres封装 V4")
_anchor("_mask_set\\.is_valid\\(\\)", cm, "构造交付正锚(cell_mark_overlay)", "pres封装 V4")
var uk := _src("res://presentation/views/ui_kit.gd")
if uk.length() > 0:
_anchor("func (gradient_base|beams_rect|switch_toggle)\\(", uk, "三工厂公开正锚(ui_kit)", "pres封装 V4")
func _walk_gd_for(dir_path: String, pattern: String, exclude: String, offenders: PackedStringArray) -> void:
var re := RegEx.create_from_string(pattern)
var dir := DirAccess.open(dir_path)
if dir == null:
return
dir.list_dir_begin()
var name := dir.get_next()
while name != "":
var full := dir_path.path_join(name)
if dir.current_is_dir():
if not name.begins_with("."):
_walk_gd_for(full, pattern, exclude, offenders)
elif name.ends_with(".gd") and full != exclude:
_scanned += 1
if re.search(FileAccess.get_file_as_string(full)) != null:
offenders.append(full)
name = dir.get_next()
dir.list_dir_end()
# ① V1 A1 三内部数据类(字段公开化——宿主直读写内类私有字段清零)
func _sweep_a1_data_classes() -> void:
# oracle: design/engineering/test-conventions.md:56 (判定上报通道——验证函数组)
for path in A1_FILES:
var spec: Dictionary = A1_FILES[path]
var src := _src(path)
if src.length() == 0:
continue
var cls: String = spec["cls"]
var inst: String = spec["inst"]
_anchor("class %s" % cls, src, "class 正锚(%s)" % cls)
_zero("%s\\._[a-zA-Z]" % inst, src, "实例私取模式零命中(%s.%s)" % [path.get_file(), inst])
for field in spec["fields"]:
_anchor("\\.%s\\b" % field, src, "公开字段正锚(%s.%s)" % [cls, field])
# 类名文件外零消费者:全 game/ .gd 递归扫描(排除三类宿主文件与本测试件自身——本件源文本含类名 token)
var hosts := ["res://presentation/views/level_map_screen.gd",
"res://presentation/views/battle_end_overlay.gd", "res://presentation/views/debug_overlay.gd"]
var offenders: PackedStringArray = []
_walk_gd("res://", hosts, offenders)
Kit.check("A1 三类文件外零消费者(全 game/ .gd 扫描面 %d 件非空前置;越界=%s)"
% [_scanned, str(offenders)], offenders.is_empty())
var _scanned := 0
func _walk_gd(dir_path: String, exclude: Array, offenders: PackedStringArray) -> void:
var dir := DirAccess.open(dir_path)
if dir == null:
return
dir.list_dir_begin()
var name := dir.get_next()
while name != "":
var full := dir_path.path_join(name)
if dir.current_is_dir():
if not name.begins_with("."):
_walk_gd(full, exclude, offenders)
elif name.ends_with(".gd") and not exclude.has(full) and not full.contains("presentation_encapsulation_sweep_test"):
_scanned += 1
var src := FileAccess.get_file_as_string(full)
for cls in ["_MapState", "_EndOverlayState", "_DebugState"]:
if src.contains(cls):
offenders.append("%s:%s" % [full, cls])
name = dir.get_next()
dir.list_dir_end()