iter51-卡3

This commit is contained in:
DKChan committed 2026-11-04 05:08:32 +08:00
1 parent 372b4fdc31
commit f9f2cb2d27
4 files changed
+79 -2

No files matched your search

+1 -2
View File
@@ -7,7 +7,6 @@
class_name NodeMapGenerator
extends RefCounted
const NODE_TYPES := ["battle", "boss", "reward", "rest", "shop"]
const FILLER_TYPES := ["battle", "reward", "rest"]
# 多终点图的支路互斥段必须各有独有类型(契约 5③),而商店与 Boss 是每条支路共有件,
# 独有类型只能来自 battle/reward/rest 三件 ⇒ 归属大岔口各区域的填充节点取互斥签名类型。
@@ -133,7 +132,7 @@ func _vo_node_fields(nodes: Array, errs: Array, by_id: Dictionary, indeg: Dictio
if by_id.has(id):
errs.append({"category": "dup_key", "message": "节点句柄重复: %d" % id})
by_id[id] = n
if not NODE_TYPES.has(String(n["type"])):
if not LevelTable.NODE_TYPES.has(String(n["type"])):
errs.append({"category": "enum", "message": "节点类型出域: %s" % n["type"]})
if int(n["depth"]) < 0:
errs.append({"category": "domain", "message": "节点深度为负: %d" % n["depth"]})
+1
View File
@@ -228,6 +228,7 @@ func _suites() -> Array:
{"path": "res://tests/unit/core/feat_vouchers_test.gd", "expect": 0}, # 凭证五件+留任二件(V9 路由逐件/F-1 存量账/V10 留任路由+AP 属性轴)
{"path": "res://tests/unit/core/settle_segments_test.gd", "expect": 0}, # 结算五段 Segment(V4:束构造/编排器接线/真实一轮走段序)
{"path": "res://tests/unit/core/encapsulation_sweep_test.gd", "expect": 0}, # 五处结构性零命中扫描〔封装5.1 V2–V7〕(架构审查 5.1 封装修复——七段对编排器零引用/五方法零残留/断点存档公开面/效果累积面/配置挂载面,非空前置+正锚)
{"path": "res://tests/unit/core/encapsulation_p2_sweep_test.gd", "expect": 0}, # NODE_TYPES 词表单点化结构性扫描〔架构P2 V1/V2〕(架构审查 P2 代码面——生成器本地副本归零+schema 唯一定点,非空前置+正锚)
{"path": "res://tests/unit/core/save_slot_isolation_test.gd", "expect": 0}, # 卡① 三槽位间隔离/进档失败恢复/宿主链引用同一(sv1/sv5/sv9/sv10)
{"path": "res://tests/unit/core/save_boot_no_activate_test.gd", "expect": 0}, # 卡① 启动态类级契约:无激活态/拒写盘面零触碰/越界拒绝(sv2/sv3)
{"path": "res://tests/unit/core/save_slot_summary_test.gd", "expect": 0}, # 卡① 摘要三项按槽窥视:异值/空槽/坏槽三态/零副作用(sv7/sv8)
@@ -0,0 +1,76 @@
# tests/unit/core/encapsulation_p2_sweep_test.gd — 架构审查 P2·NODE_TYPES 词表单点化结构性扫描宿主
# 断言面 = 两组(均带「扫描面非空前置+正锚」):
# V1 node_map_generator:非空+本地 const NODE_TYPES 定义零命中+LevelTable.NODE_TYPES.has( 正锚
# (词表唯一落点归 schema owner——表行校验 LevelTable;生成器 class_name 直引=S3 豁免面在案形态)
# V2 生产三目录(core/data/presentation)`NODE_TYPES :=` 定义恰好 1 处且落 data/schemas/level_table.gd
# 手法沿 encapsulation_sweep_test 源 token 扫描先例(两卡独立 worktree 独立批——不并件)。
# 运行:godot --headless --path game --script res://tests/unit/core/encapsulation_p2_sweep_test.gd
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
func _init() -> void:
_sweep_generator()
_sweep_production_face()
quit(Kit.finish("encapsulation_p2_sweep_test"))
func _src(path: String) -> String:
var src := FileAccess.get_file_as_string(path)
Kit.check("扫描面非空前置:%s" % path, src.length() > 0)
return src
func _zero(pattern: String, src: String, label: String) -> void:
var re := RegEx.create_from_string(pattern)
Kit.check("%s:%s 零命中〔架构P2〕" % [label, pattern], re.search(src) == null)
func _anchor(pattern: String, src: String, label: String) -> void:
var re := RegEx.create_from_string(pattern)
Kit.check("%s:%s 正锚在案〔架构P2〕" % [label, pattern], re.search(src) != null)
# ① V1 生成器词表定义删除与改指
func _sweep_generator() -> void:
var src := _src("res://core/node_map_generator.gd")
if src.length() == 0:
return
_zero("(?m)^const NODE_TYPES", src, "生成器本地词表副本归零")
_anchor("LevelTable\\.NODE_TYPES\\.has\\(", src, "schema 词表直引在用")
# ② V2 生产三目录定义唯一性(递归扫 .gd,恰好 1 处且落 level_table.gd)
func _sweep_production_face() -> void:
var files: Array[String] = []
for dir_path in ["res://core", "res://data", "res://presentation"]:
_collect_gd(dir_path, files)
Kit.check("扫描面非空前置:生产三目录 .gd ≥ 1 件", files.size() > 0)
var re := RegEx.create_from_string("NODE_TYPES\\s*:?=")
var hits: Array[String] = []
for f in files:
var src := FileAccess.get_file_as_string(f)
if src.length() > 0 and re.search(src) != null:
hits.append(f)
Kit.check("生产面 NODE_TYPES 定义恰好 1 处(实得 %d)〔架构P2〕" % hits.size(), hits.size() == 1)
if hits.size() == 1:
Kit.check("唯一定义落 data/schemas/level_table.gd〔架构P2〕", hits[0] == "res://data/schemas/level_table.gd")
# 递归收集 .gd(跳过 .godot 缓存与 .uid 边车)
func _collect_gd(dir_path: String, out: Array[String]) -> 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 + "/" + name
if dir.current_is_dir():
if not name.begins_with("."):
_collect_gd(full, out)
elif name.ends_with(".gd"):
out.append(full)
name = dir.get_next()
dir.list_dir_end()
@@ -0,0 +1 @@
uid://50nvccb1clcg