199 lines
10 KiB
GDScript
199 lines
10 KiB
GDScript
# 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:
|
||
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:
|
||
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()
|