test(testhealth): 替身竞态加锁 + 墙钟断言硬度(M33)

四处测试健康债务,只改 *_test.go,不动生产代码、不降并发:

- channelbridge:stubChatter / stubSessions 的跨 goroutine 字段一律经 mu 访问
  (Run/GetOrCreate 在车道 worker 上写、测试 goroutine 读,两者无 happens-before;
  负向断言「等一会儿再看循环有没有被驱动」尤其如此)。读写走访问器,Messages()
  返回副本;t.Parallel 与并发度不变。
- plugins/tools/io:claimProbeTimeout 2s(真实时间预算)→ 60s,并由 claimProbeWait
  按 t.Deadline 收缩。探针只等 goroutine 被调度(无时间语义),放大余量把
  「机器快慢」从断言里去掉,死锁仍以断言失败暴露。
- backthink:TestSelfWakeGate_WindowExpiry 窗口 120ms → 6s(给 waitFor 的 4s 上界
  留 2s 余量),到期等待改为按首次受理时刻推算 window+250ms,不再盲睡 3×窗口。
  另修 TestSelfWakeStatus_MatchesReceiptAndTracksPendingSlot:worker 先 take 再进
  Think,卡 Think 并不占待跑槽 —— 改为「上一轮把 worker 占住再断言 Pending」。
- memory:墙钟实测用例显式给闸 5s 预算(measureGateBudget),不再因默认 150ms
  超时 fail-open 打炸「每轮都真扫真判」前置断言;并把 GateFailed 纳入该断言。
This commit is contained in:
AskaEth committed 2026-10-24 22:24:14 +08:00
1 parent da6a365222
commit 7d9b2132bb
18 files changed
+407 -242

No files matched your search

+34 -5
View File
@@ -20,9 +20,38 @@ import (
// 带闸门的工具包装、以及等待与断言助手。夹具只搭台子,不写断言语义 —— 读共享 / 写独占的
// 判定一律由**真实的**进程内占用表给出(tool.NewFileClaimer),不拿自造的假表去测。
// claimProbeTimeout 是「第一路必须进入闸门 / 一路必须结束」这类断言的等待上限:足够宽
// 以免依赖机器速度,又足够短以免偶发死锁拖成整包超时。
const claimProbeTimeout = 2 * time.Second
// claimProbeTimeout 是「第一路必须进入闸门 / 一路必须结束」这类断言的等待上限。
//
// 这些等待**不含时间语义**:runLeg 起的那一路只要被调度到,就会立刻 close(entered)
// 或把结果写进 chan ——「等多久」与被测行为无关,唯一要防的是真死锁时永远等下去。
// 旧值 2s 却是一个**真实时间预算**:73 个测试包并行抢核时,一个刚起的 goroutine 被
// 饿上几秒并非不可想象(本轮其他证人据此报过假红;本机 12 核上 4 路并发 + 全树并行
// 压测未复现,故按余量加固,不拿复现驱动)。
//
// 现取值:默认 60s,即旧值的 30 倍余量(与「整树并行」量级相称:正常调度是微秒级,
// 需要 60s 才轮到调度意味着机器已经不可用);同时由 claimProbeWait 按 t.Deadline
// 的剩余预算收缩,真死锁仍以**断言失败**暴露,不会拖成整包超时。
const claimProbeTimeout = 60 * time.Second
// claimProbeFloor 是探针等待的下限:即使已经接近 t.Deadline,也至少等这么久再判失败
// (「断言失败 + 原因」比「整包超时」可读得多)。
const claimProbeFloor = 5 * time.Second
// claimProbeWait 返回本次探针的等待上限:默认 claimProbeTimeout,接近 t.Deadline 时
// 收缩到「剩余预算的一半」,使等待永远发生在包超时之内。
func claimProbeWait(t *testing.T) time.Duration {
t.Helper()
wait := claimProbeTimeout
if dl, ok := t.Deadline(); ok {
if half := time.Until(dl) / 2; half < wait {
wait = half
}
}
if wait < claimProbeFloor {
wait = claimProbeFloor
}
return wait
}
// legResult 是一路调用的结果。
type legResult struct {
@@ -228,7 +257,7 @@ func awaitEnter(t *testing.T, ch <-chan struct{}) {
t.Helper()
select {
case <-ch:
case <-time.After(claimProbeTimeout):
case <-time.After(claimProbeWait(t)):
t.Fatal("第一路没有进入执行")
}
}
@@ -239,7 +268,7 @@ func awaitDone(t *testing.T, ch <-chan legResult) legResult {
select {
case res := <-ch:
return res
case <-time.After(claimProbeTimeout):
case <-time.After(claimProbeWait(t)):
t.Fatal("这一路没有结束")
return legResult{}
}