并master(闸机提速与防作废批合回)——02分片并行+代码域起手自检+worktree素材同步,与对拍退役批同改闸脚本,冲突就地解
This commit is contained in:
commit
6b17d8f307
4 files changed
+188
-15
No files matched your search
+77
-2
@@ -4,6 +4,14 @@
|
||||
# 零命中/空值=用法错 quit(2)。两个来源分开定性——开发期直跑(默认)仅供最窄确认,不产闸回执、
|
||||
# 不作验收凭据;闸机关联档选跑(--only-source=gates,由 tools/run_all_gates.sh 在 02 闸传入)
|
||||
# 是收口验收跑批本体,回执 scope=related。无任何选跑参数=全量态,行为与全量口径完全一致。
|
||||
# 分片态:追加 `-- --shard=k --shard-count=n`(须成对,1≤k≤n,n≥2 才有意义)——把选套集
|
||||
# (--only 过滤后)切成 n 片互斥子集,本进程只跑第 k 片;闸机据此多进程并行缩短 02 闸墙钟。
|
||||
# 分片分配走「串行道优先」:开 RNG 事件日志会话的套件(SERIAL_PATTERNS,new_adventure 调用方)
|
||||
# 全部钉第 1 片——事件日志按种子命名同文件追加(user://logs/rng_log_<seed>.jsonl),跨片并行
|
||||
# 写同一文件互踩内容,断言日志纯净序的套件(cross_system_flow)必读错行。新增日志会话套件必须
|
||||
# 同步登记 SERIAL_PATTERNS,漏登记=并行期日志互踩假红。空片合法(--only 小集 × 多片):空片
|
||||
# 直接 quit(0) 放行,判定职责由其余片承担。各片独立退出码,由闸机聚合;片内三硬闸与
|
||||
# 快速失败语义不变,逐套件日志名加 _s<k> 段防跨片覆写。
|
||||
# 协议:套件清单逐条"套件 → 期望退出码";致命类独立进程(expect=1)。
|
||||
# 执行形态:逐套件起独立 Godot 子进程实跑
|
||||
#(OS.get_executable_path() 解析本引擎二进制——--script 模式无 autoload/全局标识符,
|
||||
@@ -30,6 +38,23 @@ const SUITE_TIMEOUT_DEFAULT_SEC := 120
|
||||
# 快速失败阈值(可调常量,初值 3):连续 N 个套件超时即中止整轮。
|
||||
const CONSECUTIVE_TIMEOUT_ABORT := 3
|
||||
|
||||
# 串行道套件(路径子串匹配):开 RNG 事件日志会话(new_adventure)的调用方——事件日志按种子
|
||||
# 命名同文件追加(user://logs/rng_log_<seed>.jsonl),跨分片并行写同一文件互踩;断言日志内容
|
||||
# 的套件(cross_system_flow)必须读到本会话纯净追加序。分片分配时全部钉第 1 片(见头注分片态)。
|
||||
# 新增日志会话套件时同步登记此处,漏登记=并行期假红。
|
||||
const SERIAL_PATTERNS: Array[String] = [
|
||||
"board_generator_test",
|
||||
"node_map_generator_test",
|
||||
"minesweeper_rules_test",
|
||||
"rng_stream_boot_test",
|
||||
"cross_system_flow_test",
|
||||
"node_map_determinism_test",
|
||||
]
|
||||
|
||||
# 分片态成员:0=未分片(单进程原路径);>0=第 _shard_k/_shard_n 片(_run_suite 取名用)。
|
||||
var _shard_k := 0
|
||||
var _shard_n := 0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# 选跑态解析:--only=<词[,词…]>(经 -- 分隔的 user args)——对套件 res:// 路径做子串匹配;
|
||||
@@ -46,6 +71,22 @@ func _init() -> void:
|
||||
only_terms.append(term)
|
||||
elif a == "--only-source=gates":
|
||||
only_from_gates = true
|
||||
elif a.begins_with("--shard="):
|
||||
var sv := a.trim_prefix("--shard=").strip_edges()
|
||||
if sv.is_valid_int():
|
||||
_shard_k = int(sv)
|
||||
elif a.begins_with("--shard-count="):
|
||||
var sn := a.trim_prefix("--shard-count=").strip_edges()
|
||||
if sn.is_valid_int():
|
||||
_shard_n = int(sn)
|
||||
if (_shard_k == 0) != (_shard_n == 0):
|
||||
push_error("[run_all] --shard 与 --shard-count 必须成对出现")
|
||||
quit(2)
|
||||
return
|
||||
if _shard_n < 0 or (_shard_n > 0 and (_shard_k < 1 or _shard_k > _shard_n)):
|
||||
push_error("[run_all] 分片参数非法(须 1≤k≤n,n≥1):k=%d n=%d" % [_shard_k, _shard_n])
|
||||
quit(2)
|
||||
return
|
||||
if has_only and only_terms.is_empty():
|
||||
push_error("[run_all] --only= 空值——用法错(选跑禁把零套件当全量)")
|
||||
quit(2)
|
||||
@@ -64,6 +105,14 @@ func _init() -> void:
|
||||
print("[run_all] GATE FILTERED RUN — 闸机关联档选跑 %d/%d 套件(--only=%s)" % [suites.size(), manifest.size(), ",".join(only_terms)])
|
||||
else:
|
||||
print("[run_all] FILTERED RUN — 选跑 %d/%d 套件(--only=%s)——仅供开发期最窄确认,不产闸回执、不作验收凭据" % [suites.size(), manifest.size(), ",".join(only_terms)])
|
||||
var sharded := _shard_n > 1
|
||||
if sharded:
|
||||
suites = _shard_suites(suites, _shard_k, _shard_n)
|
||||
if suites.is_empty():
|
||||
print("[run_all] SHARD %d/%d — 本片零套件,空片放行(判定职责由其余片承担)" % [_shard_k, _shard_n])
|
||||
quit(0)
|
||||
return
|
||||
print("[run_all] SHARD %d/%d — 本片 %d 套件" % [_shard_k, _shard_n, suites.size()])
|
||||
print("[run_all] unit/contract suite entry — %d suites" % suites.size())
|
||||
if DisplayServer.get_name() != "headless":
|
||||
push_warning("[run_all] 建议以 --headless 运行(D2 冒烟协议)")
|
||||
@@ -101,7 +150,10 @@ func _init() -> void:
|
||||
push_error("[run_all] %d 个套件失败" % failures)
|
||||
quit(1)
|
||||
return
|
||||
print("[run_all] all green (%d suites%s)" % [suites.size(), ",filtered" if filtered else ""])
|
||||
var tag := ",filtered" if filtered else ""
|
||||
if sharded:
|
||||
tag += ",shard %d/%d" % [_shard_k, _shard_n]
|
||||
print("[run_all] all green (%d suites%s)" % [suites.size(), tag])
|
||||
quit(0)
|
||||
|
||||
|
||||
@@ -203,7 +255,8 @@ func _run_suite(suite: Dictionary, idx: int, filtered: bool = false) -> Dictiona
|
||||
# 后又没有退出码读取 API,故由 cmd /V:ON 在套件退出后把 !ERRORLEVEL! 回显落盘,
|
||||
# 值与阻塞式 execute 经 cmd 回传的同一。
|
||||
var proj := ProjectSettings.globalize_path("res://")
|
||||
var log := proj + "/tests/.gates/run_all%s_%02d.log" % ["_filter" if filtered else "", idx]
|
||||
var shard_tag := "" if _shard_n == 0 else "_s%d" % _shard_k
|
||||
var log := proj + "/tests/.gates/run_all%s%s_%02d.log" % ["_filter" if filtered else "", shard_tag, idx]
|
||||
var codefile := log + ".code"
|
||||
if FileAccess.file_exists(codefile):
|
||||
DirAccess.remove_absolute(codefile) # 陈值防护:上轮临界残件不许冒充本轮退出码
|
||||
@@ -293,6 +346,28 @@ func _filter_suites(manifest: Array, terms: PackedStringArray) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
# 分片分配:串行道套件(SERIAL_PATTERNS 命中)全钉第 1 片;其余按片轮转(片内相对序 j%n+1)。
|
||||
# 分配对每个 k 确定性、互斥且完备——各片并起来=传入全集零重复零遗漏。
|
||||
func _shard_suites(suites: Array, k: int, n: int) -> Array:
|
||||
var out: Array = []
|
||||
var j := 0
|
||||
for suite in suites:
|
||||
var path: String = suite.get("path", "")
|
||||
var is_serial := false
|
||||
for pat in SERIAL_PATTERNS:
|
||||
if path.contains(pat):
|
||||
is_serial = true
|
||||
break
|
||||
if is_serial:
|
||||
if k == 1:
|
||||
out.append(suite)
|
||||
else:
|
||||
if j % n + 1 == k:
|
||||
out.append(suite)
|
||||
j += 1
|
||||
return out
|
||||
|
||||
|
||||
# 引擎二进制必须取 console 版:OS.get_executable_path() 返回的是 GUI 子系统 exe
|
||||
#(Godot_v4.7.2-stable_win64.exe),它起的子进程 stdout/stderr 句柄无效——输出全丢,
|
||||
# 脚本错误扫描就成了扫空气。GODOT_BIN 优先,其次按命名推导 *_console.exe。
|
||||
|
||||
Reference in new issue
Block a user